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