--[[ Copyright (c) 2012-2013 Kaarle Ritvanen See LICENSE file for license details --]] local topology = require('acf2.model.root').topology local pth = require('acf2.path') local util = require('acf2.util') local copy = util.copy local function aug_path(path) return pth.join('/files', unpack(path)) end local function strip_name(name) return type(name) == 'string' and name:match('^[^][/=)%s]+') or name end local function ipath(path, index) return path..'['..index..']' end local backend = require('acf2.object').class() function backend:init() self.aug = require('augeas').init() end function backend:find(path, leaf) util.map( function(comp) assert( comp == strip_name(comp) and ( type(comp) == 'number' or not comp:match('^%.+$') ) ) end, path ) local res = aug_path(path) if #self.aug:match(res) == 0 and #path > 1 and leaf then local index = path[#path] if type(index) == 'number' then local ppath = copy(path) table.remove(ppath) ppath = aug_path(ppath) if #self.aug:match(ppath) > 0 and #self.aug:match( ppath..'/*' ) == 0 then return ipath(ppath, index), ppath, index end end end return res end function backend:get(path, top) local tpe = top and top.type local leaf = tpe and tpe ~= 'table' local apath, mvpath = self:find(path, leaf or not tpe) local matches = self.aug:match(apath) if mvpath then assert(#matches < 2) leaf = true end if #matches == 0 then return end if #matches > 1 then assert(not leaf) local res = {} path = copy(path) for i, _ in ipairs(matches) do table.insert(path, i) if self:get(path) then table.insert(res, i) end table.remove(path) end return res end local value = self.aug:get(matches[1]) if value then return tpe == 'table' and {1} or value end if leaf then return end local names = {} for _, child in ipairs(self.aug:match(apath..'/*')) do names[strip_name(pth.name(child))] = true end return util.keys(names) end function backend:set(mods) local gcpaths = {} for _, mod in ipairs(mods) do local path, value = unpack(mod) local delete = value == nil self.aug:rm(aug_path(path)..(delete and '' or '/*')) local apath, mvpath, index = self:find(path, type(value) ~= 'table') local mpath = mvpath or apath if not delete then if #self.aug:match(mpath) == 0 then local function order(path) return topology('/augeas'..path).order end local ord = order(pth.join('/', unpack(path))) for _, sibling in ipairs(self.aug:match(pth.parent(mpath)..'/*')) do if order(strip_name(sibling):sub(7, -1)) > ord then self.aug:insert(sibling, pth.name(mpath), true) break end end end if mvpath then local size = #self.aug:match(mvpath) while size < index do self.aug:insert(ipath(mvpath, size), pth.name(mvpath)) size = size + 1 end end end if type(value) == 'table' then value = nil end if not delete or mvpath then self.aug:set(apath, value) elseif apath > '/' then apath = pth.parent(apath) end if delete or value == '' then gcpaths[mpath] = true end end local function gc(path) if path == '/' or #self.aug:match(path..'/*') > 0 then return end if self.aug:rm(path.."[. = '']") > 0 then gc(pth.parent(path)) end end for p, _ in pairs(gcpaths) do gc(p) end if self.aug:save() ~= 0 then print('Augeas save failed') for _, ep in ipairs(self.aug:match('/augeas//error')) do print(ep, self.aug:get(ep)) end assert(false) end end return backend