--[[
-UCI Validation Layer - Main Library
+UCI Validation Layer - Dependency helper
(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
(c) 2008 Steven Barth <steven@midlink.org>
]]--
-module( "luci.uvl.dependencies", package.seeall )
+local uvl = require "luci.uvl"
+local ERR = require "luci.uvl.errors"
+local util = require "luci.util"
+local table = require "table"
+
+local type, unpack = type, unpack
+local ipairs, pairs = ipairs, pairs
+
+module "luci.uvl.dependencies"
-local function _assert( condition, fmt, ... )
- if not condition then
- return assert( nil, string.format( fmt, ... ) )
- else
- return condition
- end
-end
function _parse_reference( r, c, s, o )
option = o
}
- for i, v in ipairs(luci.util.split(r,".")) do
- table.insert( ref, (v:gsub( "%$(.+)", function(n) return vars[n] end )) )
+ for v in r:gmatch("[^.]+") do
+ ref[#ref+1] = (v:gsub( "%$(.+)", vars ))
end
-
- if #ref == 1 and c and s then
- ref = { c, s, ref[1] }
- elseif #ref == 2 and c then
- ref = { c, unpack(ref) }
- elseif #ref ~= 3 then
- print("INVALID REFERENCE: "..#ref, c, s, o)
- ref = nil
+
+ if #ref < 2 then
+ table.insert(ref, 1, s or '$section')
+ end
+ if #ref < 3 then
+ table.insert(ref, 1, c or '$config')
end
return ref
end
-function check_dependency( self, uci, conf, sect, optn, nodeps, section2 )
+function _serialize_dependency( dep, v )
+ local str
+
+ for k, v in util.spairs( dep,
+ function(a,b)
+ a = ( type(dep[a]) ~= "boolean" and "_" or "" ) .. a
+ b = ( type(dep[b]) ~= "boolean" and "_" or "" ) .. b
+ return a < b
+ end
+ ) do
+ str = ( str and str .. " and " or "" ) .. k ..
+ ( type(v) ~= "boolean" and "=" .. v or "" )
+ end
+
+ return str
+end
+
+function check( self, object, nodeps )
--- print( "Depency check: ", conf .. '.' .. sect .. ( optn and '.' .. optn or '' ) )
+ local derr = ERR.DEPENDENCY(object)
- local key = conf .. '.' .. sect .. ( optn and '.' .. optn or '' )
- if not self.beenthere[key] then
- self.beenthere[key] = true
+ if not self.depseen[object:cid()] then
+ self.depseen[object:cid()] = true
else
- print("CIRCULAR DEPENDENCY!")
- return false, "Recursive depency detected"
+ return false, derr:child(ERR.DEP_RECURSIVE(object))
end
- -- check for config
- if not self.packages[conf] then self:read_scheme(conf) end
- local item = self.packages[conf]
-
- -- check for section
- if sect then
- item = _assert( self:_scheme_section( uci, conf, sect ) or self:_scheme_section( uci, conf, section2 ),
- "Unknown section '%s' in scheme '%s' requested",
- sect or '<nil>', conf or '<nil>' )
-
- -- check for option
- if optn then
- item = _assert( self:_scheme_option( uci, conf, sect, optn ) or
- self:_scheme_option( uci, conf, section2, optn ),
- "Unknown variable '%s' in scheme '%s', section '%s' requested",
- optn or '<nil>', conf or '<nil>', sect or '<nil>' )
+ if object:scheme('depends') then
+ local ok = true
+ local valid = false
+
+ for _, dep in ipairs(object:scheme('depends')) do
+ local subcondition = true
+ for k, v in pairs(dep) do
+ -- XXX: better error
+ local ref = _parse_reference( k, unpack(object.cref) )
+
+ if not ref then
+ return false, derr:child(ERR.SME_BADDEP(object,k))
+ end
+
+ local option = uvl.option( self, object.c, unpack(ref) )
+
+ valid, err = self:_validate_option( option, true )
+ if valid then
+ if not (
+ ( type(v) == "boolean" and option:value() ) or
+ ( ref[3] and option:value() ) == v
+ ) then
+ subcondition = false
+
+ local depstr = _serialize_dependency( dep, v )
+ derr:child(
+ type(v) == "boolean"
+ and ERR.DEP_NOVALUE(option, depstr)
+ or ERR.DEP_NOTEQUAL(option, {depstr, v})
+ )
+
+ break
+ end
+ else
+ subcondition = false
+
+ local depstr = _serialize_dependency( dep, v )
+ derr:child(ERR.DEP_NOTVALID(option, depstr):child(err))
+
+ break
+ end
+ end
+
+ if subcondition then
+ ok = true
+ break
+ else
+ ok = false
+ end
end
- end
- if item.depends then
- local ok = false
- local valid, err
+ if not ok then
+ return false, derr
+ end
+ else
+ return true
+ end
- for _, dep in ipairs(item.depends) do
- --print("DEP:",luci.util.serialize_data(dep))
+ if object:scheme("type") == "enum" and
+ object:scheme("enum_depends")[object:value()]
+ then
+ local ok = true
+ local valid = false
+ local enum = object:enum()
+ local eerr = ERR.DEP_BADENUM(enum)
+ for _, dep in ipairs(enum:scheme('enum_depends')[object:value()]) do
local subcondition = true
-
for k, v in pairs(dep) do
-- XXX: better error
- local ref = _assert( _parse_reference(k,conf,sect,optn),
- "Ambiguous dependency reference '" .. k .. "' given" )
+ local ref = _parse_reference( k, unpack(object.cref) )
+
+ if not ref then
+ return false, derr:child(eerr:child(ERR.SME_BADDEP(enum,k)))
+ end
+
+ local option = luci.uvl.option( self, object.c, unpack(ref) )
- -- XXX: true -> nodeps
- valid, err = self:validate_option(ref[1], ref[2], ref[3], uci, true, section2)
+ valid, err = self:_validate_option( option, true )
if valid then
- --print("CHK:",uci[ref[2]][ref[3]],v,unpack(ref))
if not (
- ( type(v) == "boolean" and uci[ref[2]][ref[3]] ) or
- ( ref[3] and uci[ref[2]][ref[3]] ) == v
+ ( type(v) == "boolean" and object.config[ref[2]][ref[3]] ) or
+ ( ref[3] and object:config() ) == v
) then
subcondition = false
- err = type(v) ~= "boolean"
- and "Option '" .. table.concat( ref, "." ) .. "' doesn't match requested type '" .. v .. '"'
- or "Option '" .. table.concat( ref, "." ) .. "' has no value"
+
+ local depstr = _serialize_dependency( dep, v )
+ eerr:child(
+ type(v) == "boolean"
+ and ERR.DEP_NOVALUE(option, depstr)
+ or ERR.DEP_NOTEQUAL(option, {depstr, v})
+ )
break
end
else
subcondition = false
+
+ local depstr = _serialize_dependency( dep, v )
+ eerr:child(ERR.DEP_NOTVALID(option, depstr):child(err))
+
break
end
end
if subcondition then
--- print( " -> Success (condition matched)\n" )
return true
+ else
+ ok = false
end
end
--- print( " -> Failed\n" )
- return false, err
+ if not ok then
+ return false, derr:child(eerr)
+ end
end
--- print( " -> Success (no depends)\n" )
return true
end