* luci/libs: uvl: invalid wrong "option not found in config" errors
[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 module( "luci.uvl.dependencies", package.seeall )
18
19 local ERR = luci.uvl.errors
20
21 function _parse_reference( r, c, s, o )
22         local ref  = { }
23         local vars = {
24                 config  = ( c or '$config'  ),
25                 section = ( s or '$section' ),
26                 option  = ( o or '$option'  )
27         }
28
29         for i, v in ipairs(luci.util.split(r,".")) do
30                 table.insert(ref, (v:gsub( "%$(.+)", function(n) return vars[n] end )))
31         end
32
33         if c or s then
34                 if #ref == 1 and c and s then
35                         ref = { c, s, ref[1] }
36                 elseif #ref == 2 and c then
37                         ref = { c, unpack(ref) }
38                 elseif #ref ~= 3 then
39                         ref = nil
40                 end
41         else
42                 if #ref == 1 then
43                         ref = { '$config', '$section', ref[1] }
44                 elseif #ref == 2 then
45                         ref = { '$config', unpack(ref) }
46                 elseif #ref ~= 3 then
47                         ref = nil
48                 end
49         end
50
51         return ref
52 end
53
54 function _serialize_dependency( dep, v )
55         local str
56
57         for k, v in luci.util.spairs( dep,
58                 function(a,b)
59                         a = ( type(dep[a]) ~= "boolean" and "_" or "" ) .. a
60                         b = ( type(dep[b]) ~= "boolean" and "_" or "" ) .. b
61                         return a < b
62                 end
63         ) do
64                 str = ( str and str .. " and " or "" ) .. k ..
65                         ( type(v) ~= "boolean" and "=" .. v or "" )
66         end
67
68         return str
69 end
70
71 function check( self, object, nodeps )
72
73         local derr = ERR.DEPENDENCY(object)
74
75         if not self.depseen[object:cid()] then
76                 self.depseen[object:cid()] = true
77         else
78                 return false, derr:child(ERR.DEP_RECURSIVE(object))
79         end
80
81         if object:scheme('depends') then
82                 local ok    = true
83                 local valid = false
84
85                 for _, dep in ipairs(object:scheme('depends')) do
86                         local subcondition = true
87                         for k, v in pairs(dep) do
88                                 -- XXX: better error
89                                 local ref = _parse_reference( k, unpack(object.cref) )
90
91                                 if not ref then
92                                         return false, derr:child(ERR.SME_BADDEP(object,k))
93                                 end
94
95                                 local option = luci.uvl.option( self, object.c, unpack(ref) )
96
97                                 valid, err = self:_validate_option( option, true )
98                                 if valid then
99                                         if not (
100                                                 ( type(v) == "boolean" and option:value() ) or
101                                                 ( ref[3] and option:value() ) == v
102                                         ) then
103                                                 subcondition = false
104
105                                                 local depstr = _serialize_dependency( dep, v )
106                                                 derr:child(
107                                                         type(v) == "boolean"
108                                                                 and ERR.DEP_NOVALUE(option, depstr)
109                                                                 or  ERR.DEP_NOTEQUAL(option, {depstr, v})
110                                                 )
111
112                                                 break
113                                         end
114                                 else
115                                         subcondition = false
116
117                                         local depstr = _serialize_dependency( dep, v )
118                                         derr:child(ERR.DEP_NOTVALID(option, depstr):child(err))
119
120                                         break
121                                 end
122                         end
123
124                         if subcondition then
125                                 ok = true
126                                 break
127                         else
128                                 ok = false
129                         end
130                 end
131
132                 if not ok then
133                         return false, derr
134                 end
135         else
136                 return true
137         end
138
139         if object:scheme("type") == "enum" and
140            object:scheme("enum_depends")[object:value()]
141         then
142                 local ok    = true
143                 local valid = false
144                 local enum  = object:enum()
145                 local eerr  = ERR.DEP_BADENUM(enum)
146
147                 for _, dep in ipairs(enum:scheme('enum_depends')[object:value()]) do
148                         local subcondition = true
149                         for k, v in pairs(dep) do
150                                 -- XXX: better error
151                                 local ref = _parse_reference( k, unpack(object.cref) )
152
153                                 if not ref then
154                                         return false, derr:child(eerr:child(ERR.SME_BADDEP(enum,k)))
155                                 end
156
157                                 local option = luci.uvl.option( self, object.c, unpack(ref) )
158
159                                 valid, err = self:_validate_option( option, true )
160                                 if valid then
161                                         if not (
162                                                 ( type(v) == "boolean" and object.config[ref[2]][ref[3]] ) or
163                                                 ( ref[3] and object:config() ) == v
164                                         ) then
165                                                 subcondition = false
166
167                                                 local depstr = _serialize_dependency( dep, v )
168                                                 eerr:child(
169                                                         type(v) == "boolean"
170                                                                 and ERR.DEP_NOVALUE(option, depstr)
171                                                                 or  ERR.DEP_NOTEQUAL(option, {depstr, v})
172                                                 )
173
174                                                 break
175                                         end
176                                 else
177                                         subcondition = false
178
179                                         local depstr = _serialize_dependency( dep, v )
180                                         eerr:child(ERR.DEP_NOTVALID(option, depstr):child(err))
181
182                                         break
183                                 end
184                         end
185
186                         if subcondition then
187                                 return true
188                         else
189                                 ok = false
190                         end
191                 end
192
193                 if not ok then
194                         return false, derr:child(eerr)
195                 end
196         end
197
198         return true
199 end