* luci/libs: uvl: fix handling of boolean options, better error descriptions, impleme...
[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 function _assert( condition, fmt, ... )
20         if not condition then
21                 return assert( nil, string.format( fmt, ... ) )
22         else
23                 return condition
24         end
25 end
26
27
28 function _parse_reference( r, c, s, o )
29         local ref  = { }
30         local vars = {
31                 config  = c,
32                 section = s,
33                 option  = o
34         }
35
36         for i, v in ipairs(luci.util.split(r,".")) do
37                 table.insert( ref, (v:gsub( "%$(.+)", function(n) return vars[n] end )) )
38         end
39
40         if #ref == 1 and c and s then
41                 ref = { c, s, ref[1] }
42         elseif #ref == 2 and c then
43                 ref = { c, unpack(ref) }
44         elseif #ref ~= 3 then
45                 ref = nil
46         end
47
48         return ref
49 end
50
51 function check( self, object, nodeps )
52
53         if not self.beenthere[object:cid()] then
54                 self.beenthere[object:cid()] = true
55         else
56                 return false, "Recursive dependency for '" .. object:sid() .. "' found"
57         end
58
59         local item = object.type == luci.uvl.TYPE_SECTION
60                 and object:section() or object:option()
61
62         if item.depends then
63                 local ok = false
64                 local valid, err = false,
65                         string.format( 'In dependency check for %s "%s":',
66                                 ( object.type == luci.uvl.TYPE_SECTION and "section" or "option" ),
67                                 object:cid() )
68
69                 for _, dep in ipairs(item.depends) do
70                         local subcondition = true
71                         for k, v in pairs(dep) do
72                                 -- XXX: better error
73                                 local ref = _parse_reference( k, unpack(object.cref) )
74
75                                 if not ref then
76                                         return false, "Ambiguous dependency reference '" .. k ..
77                                                 "' for object '" .. object:sid() .. "' given"
78                                 end
79
80                                 local option = luci.uvl.option(
81                                         self, object.config,
82                                         object.config[ref[2]]
83                                                 and object.config[ref[2]]['.type']
84                                                 or  object.sref[2],
85                                         ref[1], ref[2], ref[3]
86                                 )
87
88                                 valid, err2 = self:_validate_option( option, true )
89                                 if valid then
90                                         if not (
91                                                 ( type(v) == "boolean" and object.config[ref[2]][ref[3]] ) or
92                                                 ( ref[3] and object.config[ref[2]][ref[3]] ) == v
93                                         ) then
94                                                 subcondition = false
95                                                 err = err .. "\n" ..
96                                                         self.log.dump_dependency( dep, ref, v )
97                                                 break
98                                         end
99                                 else
100                                         subcondition = false
101                                         err = err .. "\n" ..
102                                                 self.log.dump_dependency( dep, ref, nil, err2 )
103                                         break
104                                 end
105                         end
106
107                         if subcondition then
108                                 return true
109                         end
110                 end
111
112                 return false, err
113         end
114
115         return true
116 end