Module:Cache

-- Imports

local libraryUtil = require( 'libraryUtil' ) local var = require( 'Module:Variables' ).var local vardefine = require( 'Module:Variables' ).vardefine

-- Strings

local i18n = { error = { unsupported_type = 'Unsupported data type' }, log = { caching = 'No cached version found. Storing.' } }

-- Helper functions

local h = {}

--- Generate a variable unique key. -- @param args (table): The template/module arguments. -- @param prefix (string): A unique prefix for the cached template/module. -- @return (string): A unique key. function h.make_key( args, prefix ) prefix = ( prefix or 'cache' ) local key = { prefix } for _,arg in pairs( args ) do   table.insert( key, arg ) end local key = table.concat( key, '_' ) return mw.hash.hashValue( 'md5', key ) end

--- Convert a value into a string for caching. -- @param value (string/number/bool/table/nil): The value to be converted -- @return (string): The converted value. -- @return (string): The original type. function h.preprocess( value ) local value_type = type( value ) if value_type == 'number' or value_type == 'boolean' then value = tostring( value ) elseif value_type == 'table' then -- Check the table for a metatable to detect mw.html and similar objects. local mt = getmetatable( value ) if mt and mt.__tostring then value = tostring( value ) value_type = 'string' else value = mw.text.jsonEncode( value ) end elseif value_type ~= 'string' then error( i18n.error.unsupported_type ) end

return value, value_type end

--- Convert a string value back to its original type. -- @param value (string): The string value. -- @param value_type (string): The original type. -- @return (string/number/bool/table/nil): The value in its original type. function h.process( value, value_type ) if value_type == 'string' then return value elseif value_type == 'number' then return tonumber( value ) elseif value_type == 'boolean' then return ( value == 'true' ) elseif value_type == 'table' then return mw.text.jsonDecode( value ) elseif not value_type then return else error( i18n.error.unsupported_type ) end end

--- Write a value to the cache. -- @param key (string): The key under which the value will be stored. -- @param value (string/number/bool/table/nil): The value to store. -- @return (string/number/bool/table/nil): The value. function h.write( key, value ) local processed_value, value_type = h.preprocess( value ) vardefine( key, processed_value ) vardefine( key .. '__type', value_type ) return value end

--- Read a value from the cache. -- @param key (string): The key from which to retrieve the value. -- @return (string/number/bool/table/nil): The original value. function h.read( key ) local value = var( key ) local value_type = var( key .. '__type' )

return h.process( value, value_type ) end

-- Functions

local p = {}

function p.auto( func, args, prefix ) libraryUtil.checkType( 'auto', 1, func, 'function' ) libraryUtil.checkType( 'auto', 2, args, 'table' ) libraryUtil.checkType( 'auto', 3, prefix, 'string', true )

local key = h.make_key( args, prefix ) local cached = h.read( key ) if cached then return cached else mw.log( i18n.log.caching ) return h.write( key, func( args ) ) end end

function p.set( args, value, prefix ) libraryUtil.checkType( 'set', 1, args, 'table' ) libraryUtil.checkTypeMulti( 'set', 2, value, { 'boolean', 'string', 'number', 'table' } ) libraryUtil.checkType( 'set', 3, prefix, 'string', true )

local key = h.make_key( args, prefix )

return h.write( key, value ) end

function p.get( args, prefix ) libraryUtil.checkType( 'get', 2, args, 'table' ) libraryUtil.checkType( 'get', 3, prefix, 'string', true )

local key = h.make_key( args, prefix )

return h.read( key ) end

-- Return

return p