--[[ Copyright (c) 2012 Kaarle Ritvanen See LICENSE file for license details --]] module(..., package.seeall) local pth = require('acf.path') local util = require('acf.persistence.util') local copy = require('acf.util').copy require 'json' require 'lfs' local function keys(tbl) local res = {} for k, v in pairs(tbl) do table.insert(res, k) end return res end backend = require('acf.object').class() function backend:init() -- TODO cache expiration self.cache = {} self.dirty = {} end function backend:split_path(path) local fpath = copy(path) local jpath = {} local res while #fpath > 0 do local fp = pth.join('/', unpack(fpath)) if self.cache[fp] then return fp, jpath end table.insert(jpath, 1, fpath[#fpath]) table.remove(fpath) end fpath = '/' while true do fpath = pth.join(fpath, jpath[1]) table.remove(jpath, 1) local attrs = lfs.attributes(fpath) if not attrs or not ({directory=true, file=true})[attrs.mode] then error('File or directory does not exist: '..fpath) end if attrs.mode == 'file' then return fpath, jpath end assert(#jpath > 0) end end function backend:_get(path) local fpath, jpath = self:split_path(path) if not self.cache[fpath] then self.cache[fpath] = json.decode(util.read_file(fpath)) end local res = self.cache[fpath] while #jpath > 0 do if res == nil then return end assert(type(res) == 'table') local next = jpath[1] res = res[tonumber(next) or next] table.remove(jpath, 1) end return res end function backend:get(path) local res = self:_get(path) return type(res) == 'table' and keys(res) or res end function backend:set(mods) local dirty = {} for _, mod in ipairs(mods) do local p, t, value = unpack(mod) local fpath, jpath = self:split_path(p) if t == 'table' then value = {} end if #jpath == 0 then self.cache[fpath] = value else local comps = copy(p) local name = comps[#comps] table.remove(comps) self:_get(comps)[tonumber(name) or name] = value end dirty[fpath] = true end for path, _ in pairs(dirty) do local file = util.open_file(path, 'w') file:write(json.encode(self.cache[path])) file:close() end end