Optimized uvl.dependencies
[project/luci.git] / libs / uvl / luasrc / uvl / dependencies.lua
1 --[[
2
3 UCI Validation Layer - Dependency helper
4 (c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
5 (c) 2008 Steven Barth <steven@midlink.org>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11         http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14
15 ]]--
16
17 local uvl = require "luci.uvl"
18 local ERR = require "luci.uvl.errors"
19 local util = require "luci.util"
20 local table = require "table"
21
22 local type, unpack = type, unpack
23 local ipairs, pairs = ipairs, pairs
24
25 module "luci.uvl.dependencies"
26
27
28
29 function _parse_reference( r, c, s, o )
30         local ref  = { }
31         local vars = {
32                 config  = c,
33                 section = s,
34                 option  = o
35         }
36
37         for v in r:gmatch("[^.]+") do
38                 ref[#ref+1] = (v:gsub( "%$(.+)", vars ))
39         end
40         
41         if #ref < 2 then
42                 table.insert(ref, 1, s or '$section')
43         end
44         if #ref < 3 then
45                 table.insert(ref, 1, c or '$config')
46         end
47
48         return ref
49 end
50
51 function _serialize_dependency( dep, v )
52         local str
53
54         for k, v in util.spairs( dep,
55                 function(a,b)
56                         a = ( type(dep[a]) ~= "boolean" and "_" or "" ) .. a
57                         b = ( type(dep[b]) ~= "boolean" and "_" or "" ) .. b
58                         return a < b
59                 end
60         ) do
61                 str = ( str and str .. " and " or "" ) .. k ..
62                         ( type(v) ~= "boolean" and "=" .. v or "" )
63         end
64
65         return str
66 end
67
68 function check( self, object, nodeps )
69
70         local derr = ERR.DEPENDENCY(object)
71
72         if not self.depseen[object:cid()] then
73                 self.depseen[object:cid()] = true
74         else
75                 return false, derr:child(ERR.DEP_RECURSIVE(object))
76         end
77
78         if object:scheme('depends') then
79                 local ok    = true
80                 local valid = false
81
82                 for _, dep in ipairs(object:scheme('depends')) do
83                         local subcondition = true
84                         for k, v in pairs(dep) do
85                                 -- XXX: better error
86                                 local ref = _parse_reference( k, unpack(object.cref) )
87
88                                 if not ref then
89                                         return false, derr:child(ERR.SME_BADDEP(object,k))
90                                 end
91
92                                 local option = uvl.option( self, object.c, unpack(ref) )
93
94                                 valid, err = self:_validate_option( option, true )
95                                 if valid then
96                                         if not (
97                                                 ( type(v) == "boolean" and option:value() ) or
98                                                 ( ref[3] and option:value() ) == v
99                                         ) then
100                                                 subcondition = false
101
102                                                 local depstr = _serialize_dependency( dep, v )
103                                                 derr:child(
104                                                         type(v) == "boolean"
105                                                                 and ERR.DEP_NOVALUE(option, depstr)
106                                                                 or  ERR.DEP_NOTEQUAL(option, {depstr, v})
107                                                 )
108
109                                                 break
110                                         end
111                                 else
112                                         subcondition = false
113
114                                         local depstr = _serialize_dependency( dep, v )
115                                         derr:child(ERR.DEP_NOTVALID(option, depstr):child(err))
116
117                                         break
118                                 end
119                         end
120
121                         if subcondition then
122                                 ok = true
123                                 break
124                         else
125                                 ok = false
126                         end
127                 end
128
129                 if not ok then
130                         return false, derr
131                 end
132         else
133                 return true
134         end
135
136         if object:scheme("type") == "enum" and
137            object:scheme("enum_depends")[object:value()]
138         then
139                 local ok    = true
140                 local valid = false
141                 local enum  = object:enum()
142                 local eerr  = ERR.DEP_BADENUM(enum)
143
144                 for _, dep in ipairs(enum:scheme('enum_depends')[object:value()]) do
145                         local subcondition = true
146                         for k, v in pairs(dep) do
147                                 -- XXX: better error
148                                 local ref = _parse_reference( k, unpack(object.cref) )
149
150                                 if not ref then
151                                         return false, derr:child(eerr:child(ERR.SME_BADDEP(enum,k)))
152                                 end
153
154                                 local option = luci.uvl.option( self, object.c, unpack(ref) )
155
156                                 valid, err = self:_validate_option( option, true )
157                                 if valid then
158                                         if not (
159                                                 ( type(v) == "boolean" and object.config[ref[2]][ref[3]] ) or
160                                                 ( ref[3] and object:config() ) == v
161                                         ) then
162                                                 subcondition = false
163
164                                                 local depstr = _serialize_dependency( dep, v )
165                                                 eerr:child(
166                                                         type(v) == "boolean"
167                                                                 and ERR.DEP_NOVALUE(option, depstr)
168                                                                 or  ERR.DEP_NOTEQUAL(option, {depstr, v})
169                                                 )
170
171                                                 break
172                                         end
173                                 else
174                                         subcondition = false
175
176                                         local depstr = _serialize_dependency( dep, v )
177                                         eerr:child(ERR.DEP_NOTVALID(option, depstr):child(err))
178
179                                         break
180                                 end
181                         end
182
183                         if subcondition then
184                                 return true
185                         else
186                                 ok = false
187                         end
188                 end
189
190                 if not ok then
191                         return false, derr:child(eerr)
192                 end
193         end
194
195         return true
196 end