summaryrefslogblamecommitdiffstats
path: root/aconf/persistence/backends/files.lua
blob: c619754b40808a46a00c2589dee0790dda41ee42 (plain) (tree)
1
2
3
4
5
6
7
8
9
    
                                       


                                    

                                                     
                                             
                                              
 

                                  

 




                                                                             


                                    


   
                                               



                                           
                               
                                                     

                               

                                        
 
                            

                                                



                                            
 


                                     
 


                                             
 
                                  
                       
                                                   
                                                          
                                                 




                                                     






                               
                                           
                                                        
 
                          

                           






                                                  
                                        
                                  

          





                                                                         
 

                                                  
                                            



                                    


         


              
--[[
Copyright (c) 2012-2015 Kaarle Ritvanen
See LICENSE file for license details
--]]

local topology = require('aconf.model.root').topology
local pth = require('aconf.path')
local address = require('aconf.path.address')
local util = require('aconf.persistence.util')

local posix = require('posix')
local stringy = require('stringy')


local function get_scope(top)
   if not top or top.type ~= 'reference' or not pth.is_unique(top.scope) then
      return
   end

   return stringy.startswith(
      top.scope, '/files/'
   ) and top.scope:sub(7, -1) or nil
end


local backend = require('aconf.object').class()

-- TODO cache expiration
function backend:init() self.cache = {} end

function backend:get(path, top)
   local name = address.join('/', table.unpack(path))

   if not self.cache[name] then
      local t = posix.stat(name, 'type')
      if not t then return end

      if t == 'regular' then
	 self.cache[name] = util.read_file(name)

      elseif t == 'link' then
	 -- TODO handle relative symlinks
	 local target = posix.readlink(name)
	 assert(target)

	 local scope = get_scope(top)
	 assert(scope)
	 scope = scope..'/'

	 local slen = scope:len()
	 assert(target:sub(1, slen) == scope)
	 return target:sub(slen + 1, -1)

      elseif t == 'directory' then
	 local res = {}
	 for _, fname in ipairs(posix.dir(name)) do
	    if not ({['.']=true, ['..']=true})[fname] then
	       table.insert(res, pth.name(fname))
	    end
	 end
	 return res

      else error('Unsupported file type: '..name) end
   end

   return self.cache[name]
end

function backend:set(mods)
   for _, mod in pairs(mods) do
      local path, value = table.unpack(mod)
      local name = address.join('/', table.unpack(path))

      if value == nil then
	 print('DEL', name)

	 local t = posix.stat(name, 'type')
	 if t == 'directory' then
	    assert(posix.rmdir(name))
	 elseif t then assert(os.remove(name)) end

	 self.cache[name] = nil

      elseif type(value) == 'table' then
	 assert(posix.mkdir(name))

      else
	 local scope = get_scope(topology('/files'..name))

	 if scope then
	    -- TODO use relative symlink
	    os.remove(name)
	    assert(posix.link(pth.to_absolute(value, scope), name, true))

	 else
	    local file = util.open_file(name, 'w')
	    file:write(util.tostring(value))
	    file:close()

	    self.cache[name] = value
	 end
      end
   end
end


return backend