| 1 | --[[ |
|---|
| 2 | |
|---|
| 3 | UCI Validation Layer - Error handling |
|---|
| 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 | local uci = require "luci.model.uci" |
|---|
| 18 | local uvl = require "luci.uvl" |
|---|
| 19 | local util = require "luci.util" |
|---|
| 20 | local string = require "string" |
|---|
| 21 | |
|---|
| 22 | local luci, tonumber, unpack, ipairs, type = |
|---|
| 23 | luci, tonumber, unpack, ipairs, type |
|---|
| 24 | |
|---|
| 25 | module "luci.uvl.errors" |
|---|
| 26 | |
|---|
| 27 | ERRCODES = { |
|---|
| 28 | UCILOAD = 'Unable to load config "%p": %1', |
|---|
| 29 | |
|---|
| 30 | SCHEME = 'Error in scheme "%p":\n%c', |
|---|
| 31 | CONFIG = 'Error in config "%p":\n%c', |
|---|
| 32 | SECTION = 'Error in section "%i" (%I):\n%c', |
|---|
| 33 | OPTION = 'Error in option "%i" (%I):\n%c', |
|---|
| 34 | REFERENCE = 'Option "%i" has invalid reference specification %1:\n%c', |
|---|
| 35 | DEPENDENCY = 'In dependency check for %t "%i":\n%c', |
|---|
| 36 | |
|---|
| 37 | SME_FIND = 'Can not find scheme "%p" in "%1"', |
|---|
| 38 | SME_READ = 'Can not access file "%1"', |
|---|
| 39 | SME_REQFLD = 'Missing required scheme field "%1" in "%i"', |
|---|
| 40 | SME_INVREF = 'Illegal reference "%1" to an anonymous section', |
|---|
| 41 | SME_BADREF = 'Malformed reference in "%1"', |
|---|
| 42 | SME_BADDEP = 'Malformed dependency specification "%1" in "%i"', |
|---|
| 43 | SME_BADVAL = 'Malformed validator specification "%1" in "%i"', |
|---|
| 44 | SME_ERRVAL = 'External validator "%1" failed: %2', |
|---|
| 45 | SME_VBADPACK = 'Variable "%o" in scheme "%p" references unknown package "%1"', |
|---|
| 46 | SME_VBADSECT = 'Variable "%o" in scheme "%p" references unknown section "%1"', |
|---|
| 47 | SME_EBADPACK = 'Enum "%v" in scheme "%p" references unknown package "%1"', |
|---|
| 48 | SME_EBADSECT = 'Enum "%v" in scheme "%p" references unknown section "%1"', |
|---|
| 49 | SME_EBADOPT = 'Enum "%v" in scheme "%p" references unknown option "%1"', |
|---|
| 50 | SME_EBADTYPE = 'Enum "%v" in scheme "%p" references non-enum option "%I"', |
|---|
| 51 | SME_EBADDEF = 'Enum "%v" in scheme "%p" redeclares the default value of "%I"', |
|---|
| 52 | |
|---|
| 53 | SECT_UNKNOWN = 'Section "%i" (%I) not found in scheme', |
|---|
| 54 | SECT_REQUIRED = 'Required section "%p.%S" not found in config', |
|---|
| 55 | SECT_UNIQUE = 'Unique section "%p.%S" occurs multiple times in config', |
|---|
| 56 | SECT_NAMED = 'The section of type "%p.%S" is stored anonymously in config but must be named', |
|---|
| 57 | SECT_NOTFOUND = 'Section "%p.%s" not found in config', |
|---|
| 58 | |
|---|
| 59 | OPT_UNKNOWN = 'Option "%i" (%I) not found in scheme', |
|---|
| 60 | OPT_REQUIRED = 'Required option "%i" has no value', |
|---|
| 61 | OPT_BADVALUE = 'Value "%1" of option "%i" is not defined in enum %2', |
|---|
| 62 | OPT_INVVALUE = 'Value "%1" of option "%i" does not validate as datatype "%2"', |
|---|
| 63 | OPT_NOTLIST = 'Option "%i" is defined as list but stored as plain value', |
|---|
| 64 | OPT_DATATYPE = 'Option "%i" has unknown datatype "%1"', |
|---|
| 65 | OPT_NOTFOUND = 'Option "%p.%s.%o" not found in config', |
|---|
| 66 | OPT_RANGE = 'Option "%p.%s.%o" is not within the specified range', |
|---|
| 67 | |
|---|
| 68 | DEP_NOTEQUAL = 'Dependency (%1) failed:\nOption "%i" is not eqal "%2"', |
|---|
| 69 | DEP_NOVALUE = 'Dependency (%1) failed:\nOption "%i" has no value', |
|---|
| 70 | DEP_NOTVALID = 'Dependency (%1) failed:\n%c', |
|---|
| 71 | DEP_RECURSIVE = 'Recursive dependency for option "%i" detected', |
|---|
| 72 | DEP_BADENUM = 'In dependency check for enum value "%i":\n%c' |
|---|
| 73 | } |
|---|
| 74 | |
|---|
| 75 | function i18n(key) |
|---|
| 76 | if luci.i18n then |
|---|
| 77 | return luci.i18n.translate(key) |
|---|
| 78 | else |
|---|
| 79 | return key |
|---|
| 80 | end |
|---|
| 81 | end |
|---|
| 82 | |
|---|
| 83 | |
|---|
| 84 | error = util.class() |
|---|
| 85 | |
|---|
| 86 | function error.__init__(self, code, pso, args) |
|---|
| 87 | |
|---|
| 88 | self.code = code |
|---|
| 89 | self.args = ( type(args) == "table" and args or { args } ) |
|---|
| 90 | |
|---|
| 91 | if util.instanceof( pso, uvl.uvlitem ) then |
|---|
| 92 | self.stype = pso.sref[2] |
|---|
| 93 | self.package, self.section, self.option, self.value = unpack(pso.cref) |
|---|
| 94 | self.object = pso |
|---|
| 95 | self.value = self.value or ( pso.value and pso:value() ) |
|---|
| 96 | else |
|---|
| 97 | pso = ( type(pso) == "table" and pso or { pso } ) |
|---|
| 98 | |
|---|
| 99 | if pso[2] then |
|---|
| 100 | local uci = uci.cursor() |
|---|
| 101 | self.stype = uci:get(pso[1], pso[2]) or pso[2] |
|---|
| 102 | end |
|---|
| 103 | |
|---|
| 104 | self.package, self.section, self.option, self.value = unpack(pso) |
|---|
| 105 | end |
|---|
| 106 | end |
|---|
| 107 | |
|---|
| 108 | function error.child(self, err) |
|---|
| 109 | if not self.childs then |
|---|
| 110 | self.childs = { err } |
|---|
| 111 | else |
|---|
| 112 | self.childs[#self.childs+1] = err |
|---|
| 113 | end |
|---|
| 114 | return self |
|---|
| 115 | end |
|---|
| 116 | |
|---|
| 117 | function error.string(self,pad) |
|---|
| 118 | pad = pad or " " |
|---|
| 119 | |
|---|
| 120 | local str = i18n(ERRCODES[self.code] or self.code) |
|---|
| 121 | :gsub("\n", "\n"..pad) |
|---|
| 122 | :gsub("%%i", self:cid()) |
|---|
| 123 | :gsub("%%I", self:sid()) |
|---|
| 124 | :gsub("%%p", self.package or '(nil)') |
|---|
| 125 | :gsub("%%s", self.section or '(nil)') |
|---|
| 126 | :gsub("%%S", self.stype or '(nil)') |
|---|
| 127 | :gsub("%%o", self.option or '(nil)') |
|---|
| 128 | :gsub("%%v", self.value or '(nil)') |
|---|
| 129 | :gsub("%%t", self.object and self.object:type() or '(nil)' ) |
|---|
| 130 | :gsub("%%T", self.object and self.object:title() or '(nil)' ) |
|---|
| 131 | :gsub("%%([1-9])", function(n) return self.args[tonumber(n)] or '(nil)' end) |
|---|
| 132 | :gsub("%%c", |
|---|
| 133 | function() |
|---|
| 134 | local s = "" |
|---|
| 135 | for _, err in ipairs(self.childs or {}) do |
|---|
| 136 | s = s .. err:string(pad.." ") .. "\n" .. pad |
|---|
| 137 | end |
|---|
| 138 | return s |
|---|
| 139 | end |
|---|
| 140 | ) |
|---|
| 141 | |
|---|
| 142 | return (str:gsub("%s+$","")) |
|---|
| 143 | end |
|---|
| 144 | |
|---|
| 145 | function error.cid(self) |
|---|
| 146 | return self.object and self.object:cid() or self.package .. |
|---|
| 147 | ( self.section and '.' .. self.section or '' ) .. |
|---|
| 148 | ( self.option and '.' .. self.option or '' ) .. |
|---|
| 149 | ( self.value and '.' .. self.value or '' ) |
|---|
| 150 | end |
|---|
| 151 | |
|---|
| 152 | function error.sid(self) |
|---|
| 153 | return self.object and self.object:sid() or self.package .. |
|---|
| 154 | ( self.stype and '.' .. self.stype or '' ) .. |
|---|
| 155 | ( self.option and '.' .. self.option or '' ) .. |
|---|
| 156 | ( self.value and '.' .. self.value or '' ) |
|---|
| 157 | end |
|---|
| 158 | |
|---|
| 159 | function error.is(self, code) |
|---|
| 160 | if self.code == code then |
|---|
| 161 | return true |
|---|
| 162 | elseif self.childs then |
|---|
| 163 | for _, c in ipairs(self.childs) do |
|---|
| 164 | if c:is(code) then |
|---|
| 165 | return true |
|---|
| 166 | end |
|---|
| 167 | end |
|---|
| 168 | end |
|---|
| 169 | return false |
|---|
| 170 | end |
|---|
| 171 | |
|---|
| 172 | function error.is_all(self, ...) |
|---|
| 173 | local codes = { ... } |
|---|
| 174 | |
|---|
| 175 | if util.contains(codes, self.code) then |
|---|
| 176 | return true |
|---|
| 177 | else |
|---|
| 178 | local equal = false |
|---|
| 179 | for _, c in ipairs(self.childs) do |
|---|
| 180 | if c.childs then |
|---|
| 181 | equal = c:is_all(...) |
|---|
| 182 | else |
|---|
| 183 | equal = util.contains(codes, c.code) |
|---|
| 184 | end |
|---|
| 185 | end |
|---|
| 186 | return equal |
|---|
| 187 | end |
|---|
| 188 | end |
|---|