b8d728657749cad2865203162af9cd03fcc3eff9
[project/luci.git] / libs / uvl / luasrc / uvl / dependencies.lua
1 --[[
2
3 UCI Validation Layer - Main Library
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                 print("INVALID REFERENCE: "..#ref, c, s, o)
46                 ref = nil
47         end
48
49         return ref
50 end
51
52 function check_dependency( self, uci, conf, sect, optn, nodeps, section2 )
53
54 --      print( "Depency check:    ", conf .. '.' .. sect .. ( optn and '.' .. optn or '' ) )
55
56         local key = conf .. '.' .. sect .. ( optn and '.' .. optn or '' )
57         if not self.beenthere[key] then
58                 self.beenthere[key] = true
59         else
60                 print("CIRCULAR DEPENDENCY!")
61                 return false, "Recursive depency detected"
62         end
63
64         -- check for config
65         if not self.packages[conf] then self:read_scheme(conf) end
66         local item = self.packages[conf]
67
68         -- check for section
69         if sect then
70                 item = _assert( self:_scheme_section( uci, conf, sect ) or self:_scheme_section( uci, conf, section2 ),
71                         "Unknown section '%s' in scheme '%s' requested",
72                         sect or '<nil>', conf or '<nil>' )
73
74                 -- check for option
75                 if optn then
76                         item = _assert( self:_scheme_option( uci, conf, sect, optn ) or
77                                                         self:_scheme_option( uci, conf, section2, optn ),
78                                 "Unknown variable '%s' in scheme '%s', section '%s' requested",
79                                 optn or '<nil>', conf or '<nil>', sect or '<nil>' )
80                 end
81         end
82
83         if item.depends then
84                 local ok = false
85                 local valid, err
86
87                 for _, dep in ipairs(item.depends) do
88                         --print("DEP:",luci.util.serialize_data(dep))
89
90                         local subcondition = true
91
92                         for k, v in pairs(dep) do
93                                 -- XXX: better error
94                                 local ref = _assert( _parse_reference(k,conf,sect,optn),
95                                         "Ambiguous dependency reference '" .. k .. "' given" )
96
97                                 -- XXX: true -> nodeps
98                                 valid, err = self:validate_option(ref[1], ref[2], ref[3], uci, true, section2)
99                                 if valid then
100                                         --print("CHK:",uci[ref[2]][ref[3]],v,unpack(ref))
101                                         if not (
102                                                 ( type(v) == "boolean" and uci[ref[2]][ref[3]] ) or
103                                                 ( ref[3] and uci[ref[2]][ref[3]] ) == v
104                                         ) then
105                                                 subcondition = false
106                                                 err = type(v) ~= "boolean"
107                                                         and "Option '" .. table.concat( ref, "." ) .. "' doesn't match requested type '" .. v .. '"'
108                                                         or  "Option '" .. table.concat( ref, "." ) .. "' has no value"
109
110                                                 break
111                                         end
112                                 else
113                                         subcondition = false
114                                         break
115                                 end
116                         end
117
118                         if subcondition then
119 --                              print( " -> Success (condition matched)\n" )
120                                 return true
121                         end
122                 end
123
124 --              print( " -> Failed\n" )
125                 return false, err
126         end
127
128 --      print( " -> Success (no depends)\n" )
129         return true
130 end