2026-07-21 19:19:52 +02:00
|
|
|
local util = { }
|
|
|
|
|
|
2026-07-28 16:41:07 +02:00
|
|
|
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
|
|
|
|
|
|
2026-07-22 18:36:02 +02:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2026-07-21 19:19:52 +02:00
|
|
|
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
|
|
|
|
|
|
2026-07-29 22:44:24 +02:00
|
|
|
util.pagerender = function(file,data,serve)
|
2026-07-22 18:36:02 +02:00
|
|
|
return function(req,res,next)
|
2026-07-23 10:01:34 +02:00
|
|
|
data = data or {opts = app.opts}
|
|
|
|
|
data.page = file:gsub(".*","")
|
2026-07-22 18:36:02 +02:00
|
|
|
res.status(200)
|
|
|
|
|
res.header('content-type','text/html; charset=utf-8')
|
|
|
|
|
res.body( util.template( LoadAsset(file),data or app) )
|
|
|
|
|
next()
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2026-07-29 22:44:24 +02:00
|
|
|
util.templateFile = function(file,data)
|
|
|
|
|
trace('templateFile(' .. file .. ')')
|
|
|
|
|
return util.template( LoadAsset(file), data)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
util.getURL = function()
|
|
|
|
|
local port = ':' .. GetPort()
|
|
|
|
|
if port == 80 then port = '' end
|
|
|
|
|
return GetScheme() .. '://' .. GetHost() .. port .. GetPath()
|
|
|
|
|
end
|
|
|
|
|
|
2026-07-21 19:19:52 +02:00
|
|
|
util.template = function(content,data)
|
2026-07-23 10:01:34 +02:00
|
|
|
local mdata = util.merge(data or app,{})
|
2026-07-29 22:44:24 +02:00
|
|
|
mdata = util.merge(mdata, {opts = app.opts})
|
2026-07-23 10:01:34 +02:00
|
|
|
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 )
|
2026-07-29 22:44:24 +02:00
|
|
|
-- set view url
|
|
|
|
|
mdata.url = mdata.url or util.getURL()
|
|
|
|
|
print_r(mdata)
|
2026-07-23 10:01:34 +02:00
|
|
|
return app.tpl( content, mdata )
|
2026-07-21 19:19:52 +02:00
|
|
|
end
|
|
|
|
|
|
2026-07-23 15:20:25 +02:00
|
|
|
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
|
|
|
|
|
|
2026-07-21 19:19:52 +02:00
|
|
|
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("<Circular Reference: %s>", 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
|
|
|
|
|
|
2026-07-28 16:41:07 +02:00
|
|
|
-- 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
|
2026-07-21 19:19:52 +02:00
|
|
|
|
|
|
|
|
function print_r(t)
|
|
|
|
|
print( util.dump(t) )
|
|
|
|
|
end
|
|
|
|
|
|
2026-07-28 16:41:07 +02:00
|
|
|
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
|
|
|
|
|
|
2026-07-29 22:44:24 +02:00
|
|
|
function util.pluck(path, tbl, default)
|
|
|
|
|
default = default or ''
|
|
|
|
|
if type(tbl) ~= "table" or type(path) ~= "string" then
|
|
|
|
|
return default
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local current = tbl
|
|
|
|
|
-- Match non-dot segments in the path key string
|
|
|
|
|
for key in string.gmatch(path, "[^.]+") do
|
|
|
|
|
if type(current) ~= "table" then
|
|
|
|
|
return default
|
|
|
|
|
end
|
|
|
|
|
current = current[key]
|
|
|
|
|
if current == nil then
|
|
|
|
|
return default
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return current
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2026-07-28 16:41:07 +02:00
|
|
|
function util.stripPath(str)
|
|
|
|
|
return str:gsub("^./",""):gsub("^/",""):gsub("/$","")
|
|
|
|
|
end
|
|
|
|
|
|
2026-07-21 19:19:52 +02:00
|
|
|
|
|
|
|
|
return util
|