local util = { } util.fileExist = function(filename) local file, err = io.open(filename, mode or "r") local exist = false if file then exist = true file:close() end return exist end util.readfile = function(filename, mode, silent) local file, err = io.open(filename, mode or "r") if not file then if not silent then print("Error opening file:", err) end return end local content = file:read("*a") file:close() return content end util.writefile = function(filename, content, mode) local file, err = io.open(filename, mode or "w") -- Default write mode if not file then return print("Error opening file:", err) end file:write(content) file:close() end util.clone = function(orig) local orig_type = type(orig) local copy if orig_type == 'table' then copy = {} for orig_key, orig_value in next, orig, nil do copy[util.clone(orig_key)] = util.clone(orig_value) end setmetatable(copy, util.clone(getmetatable(orig))) else -- number, string, boolean, etc copy = orig end return copy end util.flatten = function(tbl, prefix, result) result = result or {} for k, v in pairs(tbl) do local new_key = (prefix or "") .. "." .. k if type(v) == "table" then util.flatten(v, new_key, result) else result[new_key] = v end end return result end util.pagerender = function(file,data) return function(req,res,next) data = data or {opts = app.opts} data.page = file:gsub(".*","") res.status(200) res.header('content-type','text/html; charset=utf-8') res.body( util.template( LoadAsset(file),data or app) ) next() end end util.template = function(content,data) local mdata = util.merge(data or app,{}) mdata = util.merge(mdata, util.flatten(app.opts,"opts") ) mdata.header_include = mdata.header_include or '' mdata.header = app.tpl( LoadAsset('page/header.html'), mdata ) mdata.footer = app.tpl( LoadAsset('page/footer.html'), mdata ) return app.tpl( content, mdata ) end util.contains = function(tab,k_or_v) local result = false foreach( tab, function(k,v) if k == k_or_v or v == k_or_v then result = true end end) return result end util.memoize = function(fn, ttl_seconds) local cached_results = nil local last_executed = 0 return function(...) local now = os.time() -- Check if cache is missing or if the TTL has passed print("now = "..now .. " last_executed = " ..last_executed) if not cached_results or (now - last_executed) >= ttl_seconds then -- Pass all input arguments and pack all return values into a table cached_results = { fn(...) } last_executed = now end -- Unpack and return the cached results return table.unpack(cached_results) end end util.cmdExist = function(cmd) if api.runtime.os == 'unixy' then cmd = cmd .. " 1>/dev/null 2>/dev/null" end local stdout, retstr, retcode = os.execute(cmd) return retcode ~= 127 end function util.merge(t1,t2) local t3 = {} foreach( t1, function(k,v) t3[k] = v end) foreach( t2, function(k,v) t3[k] = v end) return t3 end function util.dump(value, indent, seen) indent = indent or "" seen = seen or {} local t = type(value) if t == "string" then return string.format("%q", value) elseif t == "boolean" or t == "number" or t == "nil" then return tostring(value) elseif t == "function" or t == "userdata" or t == "thread" then return string.format("<%s: %s>", t, tostring(value)) elseif t == "table" then if seen[value] then return string.format("", tostring(value)) end seen[value] = true local result = "{\n" local next_indent = indent .. " " local seen_keys = {} for i, v in ipairs(value) do seen_keys[i] = true result = result .. next_indent .. util.dump(v, next_indent, seen) .. ",\n" end for k, v in pairs(value) do if not seen_keys[k] then local key_str if type(k) == "string" and k:match("^[%a_][%w_]*$") then key_str = k else key_str = "[" .. util.dump(k, next_indent, seen) .. "]" end result = result .. next_indent .. key_str .. " = " .. util.dump(v, next_indent, seen) .. ",\n" end end if result:sub(-2) == ",\n" then result = result:sub(1, -3) .. "\n" end return result .. indent .. "}" end end function util.count(self) local i = 0 if self == nil then return i end if type(self) == 'table' then for _, _ in pairs(self) do i = i + 1 end return i else return #self end end util.call = function(fn,a,b,c,d,e,f) return function(k,v) if v ~= nil and v[fn] then v[fn](a,b,c,d,e,f) end end end function foreach(t, f, sort) if t ~= nil then for k, v in pairs(t) do f(k, v) end end end function trace(o) if os.getenv('DEBUG') then if type(o) == 'table' then print_r(o) else print(o) end end end -- usage: result = filter({ {foo=1},{foo=2} }, function(k,v) return v.foo > 1 end) -- {{foo=2}} -- result = filter({ {foo=1},{foo=2} }, {foo=1}) -- {{foo=1}} function util.filter(t,t_or_func) local result = {} foreach(t, function(k,v) if type(t_or_func) == "function" then if t_or_func(k,v) then result[k] = v end else local match = 0 foreach( t_or_func, function(kk,vv) if v[kk] == vv then result[k] = v end end) if match == util.count(t_or_func) then result[k] = v end end end) return result end function util.countChar(char, str) local _, nchars = str:gsub(char,"") return nchars end function print_r(t) print( util.dump(t) ) end function util.split(inputstr, sep) if sep == nil then sep = "%s" end local t = {} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function util.stripPath(str) return str:gsub("^./",""):gsub("^/",""):gsub("/$","") end return util