* luci/libs: uvl: implement dependencies for enum values
[project/luci.git] / libs / uvl / luasrc / uvl.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
18 --- UVL - UCI Validation Layer
19 -- @class module
20 -- @cstyle instance
21
22 module( "luci.uvl", package.seeall )
23
24 require("luci.fs")
25 require("luci.util")
26 require("luci.model.uci")
27 require("luci.uvl.loghelper")
28 require("luci.uvl.datatypes")
29 require("luci.uvl.validation")
30 require("luci.uvl.dependencies")
31
32
33 TYPE_SECTION = 0x01
34 TYPE_VARIABLE = 0x02
35 TYPE_ENUM = 0x03
36
37 --- Boolean; default true;
38 -- treat sections found in config but not in scheme as error
39 STRICT_UNKNOWN_SECTIONS = true
40
41 --- Boolean; default true;
42 -- treat options found in config but not in scheme as error
43 STRICT_UNKNOWN_OPTIONS = true
44
45 --- Boolean; default true;
46 -- treat failed external validators as error
47 STRICT_EXTERNAL_VALIDATORS = true
48
49 --- Boolean; default true;
50 -- treat list values stored as options like errors
51 STRICT_LIST_TYPE = true
52
53
54 local default_schemedir = "/lib/uci/schema"
55
56 local function _assert( condition, fmt, ... )
57 if not condition then
58 return assert( nil, string.format( fmt, ... ) )
59 else
60 return condition
61 end
62 end
63
64
65 --- Object constructor
66 -- @class function
67 -- @name UVL
68 -- @param schemedir Path to the scheme directory (optional)
69 -- @return Instance object
70 UVL = luci.util.class()
71
72 function UVL.__init__( self, schemedir )
73 self.schemedir = schemedir or default_schemedir
74 self.packages = { }
75 self.beenthere = { }
76 self.uci = luci.model.uci
77 self.dep = luci.uvl.dependencies
78 self.log = luci.uvl.loghelper
79 self.datatypes = luci.uvl.datatypes
80 end
81
82
83 --- Parse given scheme and return the scheme tree.
84 -- @param scheme Name of the scheme to parse
85 -- @return Table containing the parsed scheme or nil on error
86 -- @return String containing the reason for errors (if any)
87 function UVL.get_scheme( self, scheme )
88 if not self.packages[scheme] then
89 local ok, err = pcall( self.read_scheme, self, scheme )
90 if not ok then
91 return nil, self.log.scheme_error( scheme, err )
92 end
93 end
94 return self.packages[scheme], nil
95 end
96
97 --- Return a table containing the dependencies of specified section or option.
98 -- @param config Name of the configuration or parsed scheme object
99 -- @param section Type of the section
100 -- @param option Name of the option (optional)
101 -- @return Table containing the dependencies or nil on error
102 -- @return String containing the reason for errors (if any)
103 function UVL.get_dependencies( self, config, section, option )
104 config = ( type(config) == "string" and self:get_scheme(config) or config )
105
106 local deps = { }
107 local dt
108
109 if not config.sections[section] then return deps end
110
111 if option and config.variables[section][option] then
112 dt = config.variables[section][option].depends
113 else
114 dt = config.sections[section].depends
115 end
116
117 if dt then
118 for _, d in ipairs(dt) do
119 local sdeps = { }
120 for k, v in pairs(d) do
121 local r = self.dep._parse_reference( k )
122 if r then
123 sdeps[r] = v
124 else
125 return nil,
126 'Ambiguous dependency reference "%s" for object "%s" given'
127 %{ k, self.log.id( config.name, section, option ) }
128 end
129 end
130 table.insert( deps, sdeps )
131 end
132 end
133 return deps
134 end
135
136 --- Validate given configuration, section or option.
137 -- @param config Name of the configuration to validate
138 -- @param section Name of the section to validate (optional)
139 -- @param option Name of the option to validate (optional)
140 -- @return Boolean indicating whether the given config validates
141 -- @return String containing the reason for errors (if any)
142 function UVL.validate( self, config, section, option )
143 if config and section and option then
144 return self:validate_option( config, section, option )
145 elseif config and section then
146 return self:validate_section( config, section )
147 elseif config then
148 return self:validate_config( config )
149 end
150 end
151
152 --- Validate given configuration.
153 -- @param config Name of the configuration to validate
154 -- @return Boolean indicating whether the given config validates
155 -- @return String containing the reason for errors (if any)
156 function UVL.validate_config( self, config )
157
158 if not self.packages[config] then
159 local ok, err = pcall( self.read_scheme, self, config )
160 if not ok then
161 return false, self.log.scheme_error( config, err )
162 end
163 end
164
165 self.uci.load_config( config )
166 self.beenthere = { }
167
168 local co = self.uci.get_all( config )
169 local sc = { }
170
171 if not co then
172 return false, 'Unable to load configuration "%s"' % config
173 end
174
175 local function _uci_foreach( type, func )
176 local ok, err
177 for k, v in pairs(co) do
178 if co[k]['.type'] == type then
179 sc[type] = sc[type] + 1
180 ok, err = func( k, v )
181 if not ok then
182 err = self.log.config_error( config, err )
183 break
184 end
185 end
186 end
187 return ok, err
188 end
189
190 for k, v in pairs( self.packages[config].sections ) do
191 sc[k] = 0
192 local ok, err = _uci_foreach( k,
193 function(s)
194 local sect = luci.uvl.section( self, co, k, config, s )
195 return self:_validate_section( sect )
196 end
197 )
198 if not ok then return false, err end
199 end
200
201 if STRICT_UNKNOWN_SECTIONS then
202 for k, v in pairs(co) do
203 if not self.beenthere[config..'.'..k] then
204 return false, self.log.config_error( config,
205 'Section "%s" not found in scheme'
206 % self.log.id( config, co[k]['.type'] ) )
207 end
208 end
209 end
210
211 for _, k in ipairs(luci.util.keys(sc)) do
212 local s = self.packages[config].sections[k]
213
214 if s.required and sc[k] == 0 then
215 return false, self.log.config_error( config,
216 'Required section "%s" not found in config' % k )
217 elseif s.unique and sc[k] > 1 then
218 return false, self.log.config_error( config,
219 'Unique section "%s" occurs multiple times in config' % k )
220 end
221 end
222
223 return true, nil
224 end
225
226 --- Validate given config section.
227 -- @param config Name of the configuration to validate
228 -- @param section Name of the section to validate
229 -- @return Boolean indicating whether the given config validates
230 -- @return String containing the reason for errors (if any)
231 function UVL.validate_section( self, config, section )
232
233 if not self.packages[config] then
234 local ok, err = pcall( self.read_scheme, self, config )
235 if not ok then
236 return false, self.log.scheme_error( config, err )
237 end
238 end
239
240 self.uci.load_config( config )
241 self.beenthere = { }
242
243 local co = self.uci.get_all( config )
244
245 if not co then
246 return false, 'Unable to load configuration "%s"' % config
247 end
248
249 if co[section] then
250 return self:_validate_section( luci.uvl.section(
251 self, co, co[section]['.type'], config, section
252 ) )
253 else
254 return false, 'Section "%s" not found in config. Nothing to do.'
255 % self.log.id( config, section )
256 end
257 end
258
259 --- Validate given config option.
260 -- @param config Name of the configuration to validate
261 -- @param section Name of the section to validate
262 -- @param option Name of the option to validate
263 -- @return Boolean indicating whether the given config validates
264 -- @return String containing the reason for errors (if any)
265 function UVL.validate_option( self, config, section, option )
266
267 if not self.packages[config] then
268 local ok, err = pcall( self.read_scheme, self, config )
269 if not ok then
270 return false, self.log.scheme_error( config, err )
271 end
272 end
273
274 self.uci.load_config( config )
275 self.beenthere = { }
276
277 local co = self.uci.get_all( config )
278
279 if not co then
280 return false, 'Unable to load configuration "%s"' % config
281 end
282
283 if co[section] and co[section][option] then
284 return self:_validate_option( luci.uvl.option(
285 self, co, co[section]['.type'], config, section, option
286 ) )
287 else
288 return false, 'Option "%s" not found in config. Nothing to do.'
289 % self.log.id( config, section, option )
290 end
291 end
292
293
294 function UVL._validate_section( self, section )
295
296 if section:values() then
297 if section:section().named == true and
298 section:values()['.anonymous'] == true
299 then
300 return false, self.log.section_error( section,
301 'The section of type "%s" is stored anonymously in config but must be named'
302 % section:sid() )
303 end
304
305 for _, v in ipairs(section:variables()) do
306 local ok, err = self:_validate_option( v )
307
308 if not ok then
309 return ok, self.log.section_error( section, err )
310 end
311 end
312
313 local ok, err = luci.uvl.dependencies.check( self, section )
314
315 if not ok then
316 return false, err
317 end
318 else
319 return false, 'Option "%s" not found in config' % section:sid()
320 end
321
322 if STRICT_UNKNOWN_OPTIONS and not section:section().dynamic then
323 for k, v in pairs(section:values()) do
324 if k:sub(1,1) ~= "." and not self.beenthere[
325 section:cid() .. '.' .. k
326 ] then
327 return false, 'Option "%s" not found in scheme'
328 % self.log.id( section:sid(), k )
329 end
330 end
331 end
332
333 return true, nil
334 end
335
336 function UVL._validate_option( self, option, nodeps )
337
338 local item = option:option()
339 local val = option:value()
340
341 if not item and not ( option:section() and option:section().dynamic ) then
342 return false, 'Option "%s" not found in scheme' % option:cid()
343
344 elseif item then
345 if item.required and not val then
346 return false, 'Mandatory variable "%s" does not have a value'
347 % option:cid()
348 end
349
350 if ( item.type == "reference" or item.type == "enum" ) and val then
351 if not item.values or not item.values[val] then
352 return false,
353 'Value "%s" of given option "%s" is not defined in %s { %s }'
354 %{ val or '<nil>', option:cid(), item.type,
355 table.concat( luci.util.keys(item.values or {}), ", " ) }
356 end
357 elseif item.type == "list" and val then
358 if type(val) ~= "table" and STRICT_LIST_TYPE then
359 return false,
360 'Option "%s" is defined as list but stored as plain value'
361 % option:cid()
362 end
363 end
364
365 if item.datatype and val then
366 if self.datatypes[item.datatype] then
367 val = ( type(val) == "table" and val or { val } )
368 for i, v in ipairs(val) do
369 if not self.datatypes[item.datatype]( v ) then
370 return false,
371 'Value%s "%s" of given option "%s" does not validate as datatype "%s"'
372 %{ ( #val>1 and ' #' .. i or '' ), v,
373 option:cid(), item.datatype }
374 end
375 end
376 else
377 return false, 'Unknown datatype "%s" encountered'
378 % item.datatype
379 end
380 end
381
382 if not nodeps then
383 return luci.uvl.dependencies.check( self, option )
384 end
385
386 local ok, err = luci.uvl.validation.check( self, option )
387 if not ok and STRICT_EXTERNAL_VALIDATORS then
388 return false, self.log.validator_error( option, err )
389 end
390 end
391
392 return true, nil
393 end
394
395 --- Find all parts of given scheme and construct validation tree.
396 -- This is normally done on demand, so you don't have to call this function
397 -- by yourself.
398 -- @param scheme Name of the scheme to parse
399 function UVL.read_scheme( self, scheme )
400 local schemes = { }
401 local files = luci.fs.glob(self.schemedir .. '/*/' .. scheme)
402
403 if files then
404 for i, file in ipairs( files ) do
405 _assert( luci.fs.access(file), "Can't access file '%s'", file )
406
407 self.uci.set_confdir( luci.fs.dirname(file) )
408 self.uci.load( luci.fs.basename(file) )
409
410 table.insert( schemes, self.uci.get_all( luci.fs.basename(file) ) )
411 end
412
413 return self:_read_scheme_parts( scheme, schemes )
414 else
415 error( 'Can not find scheme "%s" in "%s"' %{ scheme, self.schemedir } )
416 end
417 end
418
419 -- Process all given parts and construct validation tree
420 function UVL._read_scheme_parts( self, scheme, schemes )
421
422 -- helper function to construct identifiers for given elements
423 local function _id( c, t )
424 if c == TYPE_SECTION then
425 return string.format(
426 'section "%s.%s"',
427 scheme, t.name or '?' )
428 elseif c == TYPE_VARIABLE then
429 return string.format(
430 'variable "%s.%s.%s"',
431 scheme, t.section or '?.?', t.name or '?' )
432 elseif c == TYPE_ENUM then
433 return string.format(
434 'enum "%s.%s.%s"',
435 scheme, t.variable or '?.?.?', t.value or '?' )
436 end
437 end
438
439 -- helper function to check for required fields
440 local function _req( c, t, r )
441 for i, v in ipairs(r) do
442 _assert( t[v], 'Missing required field "%s" in %s', v, _id(c, t) )
443 end
444 end
445
446 -- helper function to validate references
447 local function _ref( c, t )
448 local k
449 if c == TYPE_SECTION then
450 k = "package"
451 elseif c == TYPE_VARIABLE then
452 k = "section"
453 elseif c == TYPE_ENUM then
454 k = "variable"
455 end
456
457 local r = luci.util.split( t[k], "." )
458 r[1] = ( #r[1] > 0 and r[1] or scheme )
459
460 _assert( #r == c, 'Malformed %s reference in %s', k, _id(c, t) )
461
462 return r
463 end
464
465 -- helper function to read bools
466 local function _bool( v )
467 return ( v == "true" or v == "yes" or v == "on" or v == "1" )
468 end
469
470 -- Step 1: get all sections
471 for i, conf in ipairs( schemes ) do
472 for k, v in pairs( conf ) do
473 if v['.type'] == 'section' then
474
475 _req( TYPE_SECTION, v, { "name", "package" } )
476
477 local r = _ref( TYPE_SECTION, v )
478
479 self.packages[r[1]] =
480 self.packages[r[1]] or {
481 ["name"] = r[1];
482 ["sections"] = { };
483 ["variables"] = { };
484 }
485
486 local p = self.packages[r[1]]
487 p.sections[v.name] = p.sections[v.name] or { }
488 p.variables[v.name] = p.variables[v.name] or { }
489
490 local s = p.sections[v.name]
491
492 for k, v2 in pairs(v) do
493 if k ~= "name" and k ~= "package" and k:sub(1,1) ~= "." then
494 if k == "depends" then
495 s["depends"] = _assert(
496 self:_read_dependency( v2, s["depends"] ),
497 'Section "%s" in scheme "%s" has malformed ' ..
498 'dependency specification in "%s"',
499 v.name or '<nil>', scheme or '<nil>', k
500 )
501 elseif k == "dynamic" or k == "unique" or
502 k == "required" or k == "named"
503 then
504 s[k] = _bool(v2)
505 else
506 s[k] = v2
507 end
508 end
509 end
510
511 s.dynamic = s.dynamic or false
512 s.unique = s.unique or false
513 s.required = s.required or false
514 s.named = s.named or false
515 end
516 end
517 end
518
519 -- Step 2: get all variables
520 for i, conf in ipairs( schemes ) do
521 for k, v in pairs( conf ) do
522 if v['.type'] == "variable" then
523
524 _req( TYPE_VARIABLE, v, { "name", "section" } )
525
526 local r = _ref( TYPE_VARIABLE, v )
527
528 local p = _assert( self.packages[r[1]],
529 'Variable "%s" in scheme "%s" references unknown package "%s"',
530 v.name, scheme, r[1] )
531
532 local s = _assert( p.variables[r[2]],
533 'Variable "%s" in scheme "%s" references unknown section "%s"',
534 v.name, scheme, r[2] )
535
536 s[v.name] = s[v.name] or { }
537
538 local t = s[v.name]
539
540 for k, v2 in pairs(v) do
541 if k ~= "name" and k ~= "section" and k:sub(1,1) ~= "." then
542 if k == "depends" then
543 t["depends"] = _assert(
544 self:_read_dependency( v2, t["depends"] ),
545 'Invalid reference "%s" in "%s.%s.%s"',
546 v2, v.name, scheme, k
547 )
548 elseif k == "validator" then
549 t["validators"] = _assert(
550 self:_read_validator( v2, t["validators"] ),
551 'Variable "%s" in scheme "%s" has malformed ' ..
552 'validator specification in "%s"',
553 v.name, scheme, k
554 )
555 elseif k == "valueof" then
556 local values, err = self:_read_reference( v2 )
557
558 _assert( values,
559 'Variable "%s" in scheme "%s" has invalid ' ..
560 'reference specification:\n%s',
561 v.name, scheme, err )
562
563 t.type = "reference"
564 t.values = values
565 elseif k == "required" then
566 t[k] = _bool(v2)
567 else
568 t[k] = t[k] or v2
569 end
570 end
571 end
572
573 t.type = t.type or "variable"
574 t.datatype = t.datatype or "string"
575 t.required = t.required or false
576 end
577 end
578 end
579
580 -- Step 3: get all enums
581 for i, conf in ipairs( schemes ) do
582 for k, v in pairs( conf ) do
583 if v['.type'] == "enum" then
584
585 _req( TYPE_ENUM, v, { "value", "variable" } )
586
587 local r = _ref( TYPE_ENUM, v )
588 local p = _assert( self.packages[r[1]],
589 'Enum "%s" in scheme "%s" references unknown package "%s"',
590 v.value, scheme, r[1] )
591
592 local s = _assert( p.variables[r[2]],
593 'Enum "%s" in scheme "%s" references unknown section "%s"',
594 v.value, scheme, r[2] )
595
596 local t = _assert( s[r[3]],
597 'Enum "%s" in scheme "%s", section "%s" references ' ..
598 'unknown variable "%s"',
599 v.value, scheme, r[2], r[3] )
600
601 _assert( t.type == "enum",
602 'Enum "%s" in scheme "%s", section "%s" references ' ..
603 'variable "%s" with non enum type "%s"',
604 v.value, scheme, r[2], r[3], t.type )
605
606 if not t.values then
607 t.values = { [v.value] = v.title or v.value }
608 else
609 t.values[v.value] = v.title or v.value
610 end
611
612 if not t.enum_depends then
613 t.enum_depends = { }
614 end
615
616 if v.default then
617 _assert( not t.default,
618 'Enum "%s" in scheme "%s", section "%s" redeclares ' ..
619 'the default value of variable "%s"',
620 v.value, scheme, r[2], v.variable )
621
622 t.default = v.value
623 end
624
625 if v.depends then
626 t.enum_depends[v.value] = _assert(
627 self:_read_dependency(
628 v.depends, t.enum_depends[v.value]
629 ),
630 'Invalid reference "%s" in "%s.%s.%s.%s"',
631 v.depends, scheme, r[2], r[3], v.value
632 )
633 end
634 end
635 end
636 end
637
638 return self
639 end
640
641 -- Read a dependency specification
642 function UVL._read_dependency( self, values, deps )
643 local expr = "%$?[a-zA-Z0-9_]+"
644 if values then
645 values = ( type(values) == "table" and values or { values } )
646 for _, value in ipairs(values) do
647 local parts = luci.util.split( value, "%s*,%s*", nil, true )
648 local condition = { }
649 for i, val in ipairs(parts) do
650 local k, v = unpack(luci.util.split(val, "%s*=%s*", nil, true))
651
652 if k and (
653 k:match("^"..expr.."%."..expr.."%."..expr.."$") or
654 k:match("^"..expr.."%."..expr.."$") or
655 k:match("^"..expr.."$")
656 ) then
657 condition[k] = v or true
658 else
659 return nil
660 end
661 end
662
663 if not deps then
664 deps = { condition }
665 else
666 table.insert( deps, condition )
667 end
668 end
669 end
670
671 return deps
672 end
673
674 -- Read a validator specification
675 function UVL._read_validator( self, values, validators )
676 if values then
677 values = ( type(values) == "table" and values or { values } )
678 for _, value in ipairs(values) do
679 local validator
680
681 if value:match("^exec:") then
682 validator = value:gsub("^exec:","")
683 elseif value:match("^lua:") then
684 validator = self:_resolve_function( (value:gsub("^lua:","") ) )
685 end
686
687 if validator then
688 if not validators then
689 validators = { validator }
690 else
691 table.insert( validators, validator )
692 end
693 else
694 return nil
695 end
696 end
697
698 return validators
699 end
700 end
701
702 -- Read a reference specification (XXX: We should validate external configs too...)
703 function UVL._read_reference( self, values )
704 local val = { }
705 values = ( type(values) == "table" and values or { values } )
706
707 for _, value in ipairs(values) do
708 local ref = luci.util.split(value, ".")
709
710 if #ref == 2 or #ref == 3 then
711 self.uci.load_config(ref[1])
712 local co = self.uci.get_all(ref[1])
713
714 if not co then
715 return nil, 'Can not load config "%s" for reference "%s"'
716 %{ ref[1], value }
717 end
718
719 for k, v in pairs(co) do
720 if v['.type'] == ref[2] then
721 if #ref == 2 then
722 if v['.anonymous'] == true then
723 return nil, 'Illegal reference "%s" to an anonymous section'
724 % value
725 end
726 val[k] = k -- XXX: title/description would be nice
727 elseif v[ref[3]] then
728 val[v[ref[3]]] = v[ref[3]] -- XXX: dito
729 end
730 end
731 end
732 else
733 return nil, 'Malformed reference "%s"' % value
734 end
735 end
736
737 return val, nil
738 end
739
740 -- Resolve given path
741 function UVL._resolve_function( self, value )
742 local path = luci.util.split(value, ".")
743
744 for i=1, #path-1 do
745 local stat, mod = pcall(require, table.concat(path, ".", 1, i))
746 if stat and mod then
747 for j=i+1, #path-1 do
748 if not type(mod) == "table" then
749 break
750 end
751 mod = mod[path[j]]
752 if not mod then
753 break
754 end
755 end
756 mod = type(mod) == "table" and mod[path[#path]] or nil
757 if type(mod) == "function" then
758 return mod
759 end
760 end
761 end
762 end
763
764
765 --- Object representation of a scheme/config section.
766 -- @class module
767 -- @cstyle instance
768 -- @name luci.uvl.section
769
770 --- Section instance constructor.
771 -- @class function
772 -- @name section
773 -- @param scheme Scheme instance
774 -- @param co Configuration data
775 -- @param st Section type
776 -- @param c Configuration name
777 -- @param s Section name
778 -- @return Section instance
779 section = luci.util.class()
780
781 function section.__init__(self, scheme, co, st, c, s)
782 self.csection = co[s]
783 self.ssection = scheme.packages[c].sections[st]
784 self.cref = { c, s }
785 self.sref = { c, st }
786 self.scheme = scheme
787 self.config = co
788 self.type = luci.uvl.TYPE_SECTION
789 end
790
791 --- Get the config path of this section.
792 -- @return String containing the identifier
793 function section.cid(self)
794 return ( self.cref[1] or '?' ) .. '.' .. ( self.cref[2] or '?' )
795 end
796
797 --- Get the scheme path of this section.
798 -- @return String containing the identifier
799 function section.sid(self)
800 return ( self.sref[1] or '?' ) .. '.' .. ( self.sref[2] or '?' )
801 end
802
803 --- Get all configuration values within this section.
804 -- @return Table containing the values
805 function section.values(self)
806 return self.csection
807 end
808
809 --- Get the associated section information in scheme.
810 -- @return Table containing the scheme properties
811 function section.section(self)
812 return self.ssection
813 end
814
815 --- Get all option objects associated with this section.
816 -- @return Table containing all associated luci.uvl.option instances
817 function section.variables(self)
818 local v = { }
819 if self.scheme.packages[self.sref[1]].variables[self.sref[2]] then
820 for o, _ in pairs(
821 self.scheme.packages[self.sref[1]].variables[self.sref[2]]
822 ) do
823 table.insert( v, luci.uvl.option(
824 self.scheme, self.config, self.sref[2],
825 self.cref[1], self.cref[2], o
826 ) )
827 end
828 end
829 return v
830 end
831
832
833 --- Object representation of a scheme/config option.
834 -- @class module
835 -- @cstyle instance
836 -- @name luci.uvl.option
837
838 --- Section instance constructor.
839 -- @class function
840 -- @name option
841 -- @param scheme Scheme instance
842 -- @param co Configuration data
843 -- @param st Section type
844 -- @param c Configuration name
845 -- @param s Section name
846 -- @param o Option name
847 -- @return Option instance
848 option = luci.util.class()
849
850 function option.__init__(self, scheme, co, st, c, s, o)
851 self.coption = co[s] and co[s][o] or nil
852 self.soption = scheme.packages[c].variables[st][o]
853 self.cref = { c, s, o }
854 self.sref = { c, st, o }
855 self.scheme = scheme
856 self.config = co
857 self.type = luci.uvl.TYPE_OPTION
858 end
859
860 --- Get the config path of this option.
861 -- @return String containing the identifier
862 function option.cid(self)
863 return ( self.cref[1] or '?' ) .. '.' ..
864 ( self.cref[2] or '?' ) .. '.' ..
865 ( self.cref[3] or '?' )
866 end
867
868 --- Get the scheme path of this option.
869 -- @return String containing the identifier
870 function option.sid(self)
871 return ( self.sref[1] or '?' ) .. '.' ..
872 ( self.sref[2] or '?' ) .. '.' ..
873 ( self.sref[3] or '?' )
874 end
875
876 --- Get the value of this option.
877 -- @return The associated configuration value
878 function option.value(self)
879 return self.coption
880 end
881
882 --- Get the associated option information in scheme.
883 -- @return Table containing the scheme properties
884 function option.option(self)
885 return self.soption
886 end
887
888 --- Get the associated section information in scheme.
889 -- @return Table containing the scheme properties
890 function option.section(self)
891 return self.scheme.packages[self.sref[1]].sections[self.sref[2]]
892 end