modlib/persistence.lua

171 lines
3.8 KiB
Lua
Raw Normal View History

-- Localize globals
2021-06-17 10:45:08 -07:00
local assert, error, io, ipairs, loadfile, math, minetest, modlib, pairs, setfenv, setmetatable, table, type = assert, error, io, ipairs, loadfile, math, minetest, modlib, pairs, setfenv, setmetatable, table, type
-- Set environment
local _ENV = {}
setfenv(1, _ENV)
2021-04-21 12:11:00 -07:00
lua_log_file = {}
local files = {}
local metatable = {__index = lua_log_file}
function lua_log_file.new(file_path, root)
2021-04-21 12:29:22 -07:00
local self = setmetatable({file_path = assert(file_path), root = root}, metatable)
if minetest then
files[self] = true
end
return self
2021-04-21 12:11:00 -07:00
end
function lua_log_file:load()
2021-04-21 12:29:22 -07:00
-- Bytecode is blocked by the engine
local read = assert(loadfile(self.file_path))
2021-04-22 02:54:25 -07:00
-- math.huge is serialized to inf
local env = {inf = math.huge}
2021-04-21 12:29:22 -07:00
setfenv(read, env)
read()
env.R = env.R or {{}}
self.reference_count = #env.R
self.root = env.R[1]
self.references = modlib.table.flip(env.R)
2021-04-21 12:11:00 -07:00
end
function lua_log_file:open()
2021-04-21 12:29:22 -07:00
self.file = io.open(self.file_path, "a+")
2021-04-21 12:11:00 -07:00
end
function lua_log_file:init()
2021-04-21 12:29:22 -07:00
if modlib.file.exists(self.file_path) then
self:load()
self:_rewrite()
self:open()
return
end
self:open()
self.root = {}
self:_write()
2021-04-21 12:11:00 -07:00
end
function lua_log_file:log(statement)
2021-04-21 12:29:22 -07:00
self.file:write(statement)
self.file:write"\n"
2021-04-21 12:11:00 -07:00
end
function lua_log_file:flush()
2021-04-21 12:29:22 -07:00
self.file:flush()
2021-04-21 12:11:00 -07:00
end
function lua_log_file:close()
2021-04-21 12:29:22 -07:00
self.file:close()
self.file = nil
files[self] = nil
2021-04-21 12:11:00 -07:00
end
2021-04-21 12:28:27 -07:00
if minetest then
2021-04-21 12:29:22 -07:00
minetest.register_on_shutdown(function()
for self in pairs(files) do
self.file:close()
end
end)
2021-04-21 12:28:27 -07:00
end
2021-04-21 12:11:00 -07:00
function lua_log_file:_dump(value, is_key)
2021-04-21 12:29:22 -07:00
if value == nil then
return "nil"
end
if value == true then
return "true"
end
if value == false then
return "false"
end
if value ~= value then
-- nan
return "0/0"
end
local _type = type(value)
if _type == "number" then
return ("%.17g"):format(value)
end
2021-05-12 11:53:44 -07:00
local reference = self.references[value]
if reference then
return "R[" .. reference .."]"
2021-04-21 12:29:22 -07:00
end
2021-05-12 11:53:44 -07:00
reference = self.reference_count + 1
local key = "R[" .. reference .."]"
2021-04-21 12:29:22 -07:00
local formatted
2021-05-26 01:33:34 -07:00
local function create_reference()
self.reference_count = reference
self.references[value] = reference
end
2021-04-21 12:29:22 -07:00
if _type == "string" then
if is_key and value:len() <= key:len() and value:match"[%a_][%a%d_]*" then
-- Short key
return value, true
end
2021-04-21 12:29:22 -07:00
formatted = ("%q"):format(value)
if formatted:len() <= key:len() then
-- Short string
return formatted
end
2021-05-26 01:33:34 -07:00
-- Use reference
create_reference()
2021-04-21 12:29:22 -07:00
elseif _type == "table" then
2021-05-26 01:33:34 -07:00
-- Tables always need a reference before they are traversed to prevent infinite recursion
create_reference()
2021-04-21 12:29:22 -07:00
local entries = {}
for _, value in ipairs(value) do
table.insert(entries, self:_dump(value))
2021-04-21 12:29:22 -07:00
end
local tablelen = #value
2021-04-21 12:29:22 -07:00
for key, value in pairs(value) do
if type(key) ~= "number" or key % 1 ~= 0 or key < 1 or key > tablelen then
local dumped, short = self:_dump(key, true)
table.insert(entries, (short and dumped or ("[" .. dumped .. "]")) .. "=" .. self:_dump(value))
2021-04-21 12:29:22 -07:00
end
end
formatted = "{" .. table.concat(entries, ";") .. "}"
else
error("unsupported type: " .. _type)
end
self:log(key .. "=" .. formatted)
return key
2021-04-21 12:11:00 -07:00
end
function lua_log_file:set(table, key, value)
2021-04-21 12:29:22 -07:00
table[key] = value
if not self.references[table] then
error"orphan table"
end
table = self:_dump(table)
local key, short_key = self:_dump(key, true)
self:log(table .. (short_key and ("." .. key) or ("[" .. key .. "]")) .. "=" .. self:_dump(value))
2021-04-21 12:11:00 -07:00
end
function lua_log_file:set_root(key, value)
2021-04-21 12:29:22 -07:00
return self:set(self.root, key, value)
2021-04-21 12:11:00 -07:00
end
function lua_log_file:_write()
2021-04-21 12:29:22 -07:00
self.references = {}
self.reference_count = 0
self:log"R={}"
self:_dump(self.root)
2021-04-21 12:11:00 -07:00
end
function lua_log_file:_rewrite()
2021-04-21 12:29:22 -07:00
self.file = io.open(self.file_path, "w+")
self:_write()
self.file:close()
2021-04-21 12:11:00 -07:00
end
function lua_log_file:rewrite()
2021-04-21 12:29:22 -07:00
if self.file then
self.file:close()
end
self:_rewrite()
2021-04-22 02:54:37 -07:00
self:open()
2021-06-17 10:45:08 -07:00
end
-- Export environment
return _ENV