* luci/libs: uvl: implement dependencies for enum values
[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 function _parse_reference( r, c, s, o )
20         local ref  = { }
21         local vars = {
22                 config  = ( c or '$config'  ),
23                 section = ( s or '$section' ),
24                 option  = ( o or '$option'  )
25         }
26
27         for i, v in ipairs(luci.util.split(r,".")) do
28                 table.insert(ref, (v:gsub( "%$(.+)", function(n) return vars[n] end )))
29         end
30
31         if c or s then
32                 if #ref == 1 and c and s then
33                         ref = { c, s, ref[1] }
34                 elseif #ref == 2 and c then
35                         ref = { c, unpack(ref) }
36                 elseif #ref ~= 3 then
37                         ref = nil
38                 end
39         else
40                 if #ref == 1 then
41                         ref = { '$config', '$section', ref[1] }
42                 elseif #ref == 2 then
43                         ref = { '$config', unpack(ref) }
44                 elseif #ref ~= 3 then
45                         ref = nil
46                 end
47         end
48
49         return ref
50 end
51
52 function check( self, object, nodeps )
53
54         if not self.beenthere[object:cid()] then
55                 self.beenthere[object:cid()] = true
56         else
57                 return false, "Recursive dependency for '" .. object:sid() .. "' found"
58         end
59
60         local item = object.type == luci.uvl.TYPE_SECTION
61                 and object:section() or object:option()
62
63         if item.depends then
64                 local ok = false
65                 local valid, err = false,
66                         string.format( 'In dependency check for %s "%s":',
67                                 ( object.type == luci.uvl.TYPE_SECTION and "section" or "option" ),
68                                 object:cid() )
69
70                 for _, dep in ipairs(item.depends) do
71                         local subcondition = true
72                         for k, v in pairs(dep) do
73                                 -- XXX: better error
74                                 local ref = _parse_reference( k, unpack(object.cref) )
75
76                                 if not ref then
77                                         return false, "Ambiguous dependency reference '" .. k ..
78                                                 "' for object '" .. object:sid() .. "' given"
79                                 end
80
81                                 local option = luci.uvl.option(
82                                         self, object.config,
83                                         object.config[ref[2]]
84                                                 and object.config[ref[2]]['.type']
85                                                 or  object.sref[2],
86                                         ref[1], ref[2], ref[3]
87                                 )
88
89                                 valid, err2 = self:_validate_option( option, true )
90                                 if valid then
91                                         if not (
92                                                 ( type(v) == "boolean" and object.config[ref[2]][ref[3]] ) or
93                                                 ( ref[3] and object.config[ref[2]][ref[3]] ) == v
94                                         ) then
95                                                 subcondition = false
96                                                 err = err .. "\n" ..
97                                                         self.log.dump_dependency( dep, ref, v )
98                                                 break
99                                         end
100                                 else
101                                         subcondition = false
102                                         err = err .. "\n" ..
103                                                 self.log.dump_dependency( dep, ref, nil, err2 )
104                                         break
105                                 end
106                         end
107
108                         if subcondition then
109                                 return true
110                         end
111                 end
112
113                 return false, err
114         end
115
116         if item.type == "enum" and item.enum_depends[object:value()] then
117                 local ok = false
118                 local valid, err = false,
119                         string.format( 'In dependency check for enum value "%s.%s":',
120                                 object:cid(), object:value() )
121
122                 for _, dep in ipairs(item.enum_depends[object:value()]) do
123                         local subcondition = true
124                         for k, v in pairs(dep) do
125                                 -- XXX: better error
126                                 local ref = _parse_reference( k, unpack(object.cref) )
127
128                                 if not ref then
129                                         return false, "Ambiguous dependency reference '" .. k ..
130                                                 "' for enum '" .. object:sid() .. "." ..
131                                                 object:value() .. "' given"
132                                 end
133
134                                 local option = luci.uvl.option(
135                                         self, object.config,
136                                         object.config[ref[2]]
137                                                 and object.config[ref[2]]['.type']
138                                                 or  object.sref[2],
139                                         ref[1], ref[2], ref[3]
140                                 )
141
142                                 valid, err2 = self:_validate_option( option, true )
143                                 if valid then
144                                         if not (
145                                                 ( type(v) == "boolean" and object.config[ref[2]][ref[3]] ) or
146                                                 ( ref[3] and object.config[ref[2]][ref[3]] ) == v
147                                         ) then
148                                                 subcondition = false
149                                                 err = err .. "\n" ..
150                                                         self.log.dump_dependency( dep, ref, v )
151                                                 break
152                                         end
153                                 else
154                                         subcondition = false
155                                         err = err .. "\n" ..
156                                                 self.log.dump_dependency( dep, ref, nil, err2 )
157                                         break
158                                 end
159                         end
160
161                         if subcondition then
162                                 return true
163                         end
164                 end
165
166                 return false, err
167         end
168
169         return true
170 end