Optimized uvl.dependencies
[project/luci.git] / libs / uvl / luasrc / uvl / dependencies.lua
index a84f73c..d19149e 100644 (file)
@@ -14,156 +14,182 @@ $Id$
 
 ]]--
 
-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"
+
+
 
 function _parse_reference( r, c, s, o )
        local ref  = { }
        local vars = {
-               config  = ( c or '$config'  ),
-               section = ( s or '$section' ),
-               option  = ( o or '$option'  )
+               config  = c,
+               section = s,
+               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 < 2 then
+               table.insert(ref, 1, s or '$section')
+       end
+       if #ref < 3 then
+               table.insert(ref, 1, c or '$config')
        end
 
-       if c or s then
-               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
-                       ref = nil
-               end
-       else
-               if #ref == 1 then
-                       ref = { '$config', '$section', ref[1] }
-               elseif #ref == 2 then
-                       ref = { '$config', unpack(ref) }
-               elseif #ref ~= 3 then
-                       ref = nil
+       return ref
+end
+
+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 ref
+       return str
 end
 
 function check( self, object, nodeps )
 
-       if not self.beenthere[object:cid()] then
-               self.beenthere[object:cid()] = true
+       local derr = ERR.DEPENDENCY(object)
+
+       if not self.depseen[object:cid()] then
+               self.depseen[object:cid()] = true
        else
-               return false, "Recursive dependency for '" .. object:sid() .. "' found"
+               return false, derr:child(ERR.DEP_RECURSIVE(object))
        end
 
-       local item = object.type == luci.uvl.TYPE_SECTION
-               and object:section() or object:option()
+       if object:scheme('depends') then
+               local ok    = true
+               local valid = false
 
-       if item.depends then
-               local ok = false
-               local valid, err = false,
-                       string.format( 'In dependency check for %s "%s":',
-                               ( object.type == luci.uvl.TYPE_SECTION and "section" or "option" ),
-                               object:cid() )
-
-               for _, dep in ipairs(item.depends) do
+               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, "Ambiguous dependency reference '" .. k ..
-                                               "' for object '" .. object:sid() .. "' given"
+                                       return false, derr:child(ERR.SME_BADDEP(object,k))
                                end
 
-                               local option = luci.uvl.option(
-                                       self, object.config,
-                                       object.config[ref[2]]
-                                               and object.config[ref[2]]['.type']
-                                               or  object.sref[2],
-                                       ref[1], ref[2], ref[3]
-                               )
+                               local option = uvl.option( self, object.c, unpack(ref) )
 
-                               valid, err2 = self:_validate_option( option, true )
+                               valid, err = self:_validate_option( option, true )
                                if valid then
                                        if not (
-                                               ( type(v) == "boolean" and object.config[ref[2]][ref[3]] ) or
-                                               ( ref[3] and object.config[ref[2]][ref[3]] ) == v
+                                               ( type(v) == "boolean" and option:value() ) or
+                                               ( ref[3] and option:value() ) == v
                                        ) then
                                                subcondition = false
-                                               err = err .. "\n" ..
-                                                       self.log.dump_dependency( dep, ref, v )
+
+                                               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
-                                       err = err .. "\n" ..
-                                               self.log.dump_dependency( dep, ref, nil, err2 )
+
+                                       local depstr = _serialize_dependency( dep, v )
+                                       derr:child(ERR.DEP_NOTVALID(option, depstr):child(err))
+
                                        break
                                end
                        end
 
                        if subcondition then
-                               return true
+                               ok = true
+                               break
+                       else
+                               ok = false
                        end
                end
 
-               return false, err
+               if not ok then
+                       return false, derr
+               end
+       else
+               return true
        end
 
-       if item.type == "enum" and item.enum_depends[object:value()] then
-               local ok = false
-               local valid, err = false,
-                       string.format( 'In dependency check for enum value "%s.%s":',
-                               object:cid(), object:value() )
+       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(item.enum_depends[object:value()]) do
+               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 = _parse_reference( k, unpack(object.cref) )
 
                                if not ref then
-                                       return false, "Ambiguous dependency reference '" .. k ..
-                                               "' for enum '" .. object:sid() .. "." ..
-                                               object:value() .. "' given"
+                                       return false, derr:child(eerr:child(ERR.SME_BADDEP(enum,k)))
                                end
 
-                               local option = luci.uvl.option(
-                                       self, object.config,
-                                       object.config[ref[2]]
-                                               and object.config[ref[2]]['.type']
-                                               or  object.sref[2],
-                                       ref[1], ref[2], ref[3]
-                               )
+                               local option = luci.uvl.option( self, object.c, unpack(ref) )
 
-                               valid, err2 = self:_validate_option( option, true )
+                               valid, err = self:_validate_option( option, true )
                                if valid then
                                        if not (
                                                ( type(v) == "boolean" and object.config[ref[2]][ref[3]] ) or
-                                               ( ref[3] and object.config[ref[2]][ref[3]] ) == v
+                                               ( ref[3] and object:config() ) == v
                                        ) then
                                                subcondition = false
-                                               err = err .. "\n" ..
-                                                       self.log.dump_dependency( dep, ref, v )
+
+                                               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
-                                       err = err .. "\n" ..
-                                               self.log.dump_dependency( dep, ref, nil, err2 )
+
+                                       local depstr = _serialize_dependency( dep, v )
+                                       eerr:child(ERR.DEP_NOTVALID(option, depstr):child(err))
+
                                        break
                                end
                        end
 
                        if subcondition then
                                return true
+                       else
+                               ok = false
                        end
                end
 
-               return false, err
+               if not ok then
+                       return false, derr:child(eerr)
+               end
        end
 
        return true