* 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