Scheme parsing optimizations
This commit is contained in:
parent
68ecfaf698
commit
c287c8ca7e
1 changed files with 226 additions and 241 deletions
|
@ -362,6 +362,7 @@ function UVL.read_scheme( self, scheme, alias )
|
|||
local files = luci.fs.glob(self.schemedir .. '/*/' .. scheme)
|
||||
|
||||
if files then
|
||||
local ok, err
|
||||
for i, file in ipairs( files ) do
|
||||
if not luci.fs.access(file) then
|
||||
return false, so:error(ERR.SME_READ(so,file))
|
||||
|
@ -369,18 +370,32 @@ function UVL.read_scheme( self, scheme, alias )
|
|||
|
||||
local uci = luci.model.uci.cursor( luci.fs.dirname(file), default_savedir )
|
||||
|
||||
local sd, err = uci:get_all( luci.fs.basename(file) )
|
||||
local sname = luci.fs.basename(file)
|
||||
local sd, err = uci:load( sname )
|
||||
|
||||
if not sd then
|
||||
return false, ERR.UCILOAD(so, err)
|
||||
end
|
||||
|
||||
table.insert( schemes, sd )
|
||||
ok, err = pcall(function()
|
||||
uci:foreach(sname, "package", function(s)
|
||||
self:_parse_package(so, s[".name"], s)
|
||||
end)
|
||||
uci:foreach(sname, "section", function(s)
|
||||
self:_parse_section(so, s[".name"], s)
|
||||
end)
|
||||
uci:foreach(sname, "variable", function(s)
|
||||
self:_parse_var(so, s[".name"], s)
|
||||
end)
|
||||
uci:foreach(sname, "enum", function(s)
|
||||
self:_parse_enum(so, s[".name"], s)
|
||||
end)
|
||||
|
||||
end)
|
||||
end
|
||||
|
||||
local ok, err = self:_read_scheme_parts( so, schemes )
|
||||
if ok and alias then self.packages[alias] = self.packages[scheme] end
|
||||
return ok, err
|
||||
return ok and self, err
|
||||
else
|
||||
return false, so:error(ERR.SME_FIND(so, self.schemedir))
|
||||
end
|
||||
|
@ -395,278 +410,248 @@ function UVL.read_scheme( self, scheme, alias )
|
|||
end
|
||||
end
|
||||
|
||||
-- Process all given parts and construct validation tree
|
||||
function UVL._read_scheme_parts( self, scheme, schemes )
|
||||
-- helper function to check for required fields
|
||||
local function _req( t, n, c, r )
|
||||
for i, v in ipairs(r) do
|
||||
if not c[v] then
|
||||
local p, o = scheme:sid(), nil
|
||||
|
||||
-- helper function to check for required fields
|
||||
local function _req( t, n, c, r )
|
||||
for i, v in ipairs(r) do
|
||||
if not c[v] then
|
||||
local p, o = scheme:sid(), nil
|
||||
|
||||
if t == TYPE_SECTION then
|
||||
o = section( scheme, nil, p, n )
|
||||
elseif t == TYPE_OPTION then
|
||||
o = option( scheme, nil, p, '(nil)', n )
|
||||
elseif t == TYPE_ENUM then
|
||||
o = enum( scheme, nil, p, '(nil)', '(nil)', n )
|
||||
end
|
||||
|
||||
return false, ERR.SME_REQFLD(o,v)
|
||||
if t == TYPE_SECTION then
|
||||
o = section( scheme, nil, p, n )
|
||||
elseif t == TYPE_OPTION then
|
||||
o = option( scheme, nil, p, '(nil)', n )
|
||||
elseif t == TYPE_ENUM then
|
||||
o = enum( scheme, nil, p, '(nil)', '(nil)', n )
|
||||
end
|
||||
|
||||
return false, ERR.SME_REQFLD(o,v)
|
||||
end
|
||||
return true
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- helper function to validate references
|
||||
local function _ref( c, t )
|
||||
local k, n
|
||||
if c == TYPE_SECTION then
|
||||
k = "package"
|
||||
n = 1
|
||||
elseif c == TYPE_OPTION then
|
||||
k = "section"
|
||||
n = 2
|
||||
elseif c == TYPE_ENUM then
|
||||
k = "variable"
|
||||
n = 3
|
||||
end
|
||||
|
||||
-- helper function to validate references
|
||||
local function _ref( c, t )
|
||||
local k, n
|
||||
if c == TYPE_SECTION then
|
||||
k = "package"
|
||||
n = 1
|
||||
elseif c == TYPE_OPTION then
|
||||
k = "section"
|
||||
n = 2
|
||||
elseif c == TYPE_ENUM then
|
||||
k = "variable"
|
||||
n = 3
|
||||
end
|
||||
local r = luci.util.split( t[k], "." )
|
||||
r[1] = ( #r[1] > 0 and r[1] or scheme:sid() )
|
||||
|
||||
local r = luci.util.split( t[k], "." )
|
||||
r[1] = ( #r[1] > 0 and r[1] or scheme:sid() )
|
||||
|
||||
if #r ~= n then
|
||||
return false, ERR.SME_BADREF(scheme, k)
|
||||
end
|
||||
|
||||
return r
|
||||
if #r ~= n then
|
||||
return false, ERR.SME_BADREF(scheme, k)
|
||||
end
|
||||
|
||||
-- helper function to read bools
|
||||
local function _bool( v )
|
||||
return ( v == "true" or v == "yes" or v == "on" or v == "1" )
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
-- helper function to read bools
|
||||
local function _bool( v )
|
||||
return ( v == "true" or v == "yes" or v == "on" or v == "1" )
|
||||
end
|
||||
|
||||
local ok, err
|
||||
-- Step 0: get package meta information
|
||||
function UVL._parse_package(self, scheme, k, v)
|
||||
local sid = scheme:sid()
|
||||
local pkg = self.packages[sid] or {
|
||||
["name"] = sid;
|
||||
["sections"] = { };
|
||||
["variables"] = { };
|
||||
}
|
||||
|
||||
-- Step 0: get package meta information
|
||||
for i, conf in ipairs( schemes ) do
|
||||
for k, v in pairs( conf ) do
|
||||
if v['.type'] == 'package' then
|
||||
self.packages[scheme:sid()] =
|
||||
self.packages[scheme:sid()] or {
|
||||
["name"] = scheme:sid();
|
||||
["sections"] = { };
|
||||
["variables"] = { };
|
||||
}
|
||||
pkg.title = v.title
|
||||
pkg.description = v.description
|
||||
|
||||
for k, v2 in pairs(v) do
|
||||
if k == "title" or k == "description" then
|
||||
self.packages[scheme:sid()][k] = v2
|
||||
end
|
||||
self.packages[sid] = pkg
|
||||
end
|
||||
|
||||
-- Step 1: get all sections
|
||||
function UVL._parse_section(self, scheme, k, v)
|
||||
local ok, err = _req( TYPE_SECTION, k, v, { "name", "package" } )
|
||||
if err then error(scheme:error(err)) end
|
||||
|
||||
local r, err = _ref( TYPE_SECTION, v )
|
||||
if err then error(scheme:error(err)) end
|
||||
|
||||
local p = self.packages[r[1]] or {
|
||||
["name"] = r[1];
|
||||
["sections"] = { };
|
||||
["variables"] = { };
|
||||
}
|
||||
p.sections[v.name] = p.sections[v.name] or { }
|
||||
p.variables[v.name] = p.variables[v.name] or { }
|
||||
self.packages[r[1]] = p
|
||||
|
||||
local s = p.sections[v.name]
|
||||
local so = scheme:section(v.name)
|
||||
|
||||
for k, v2 in pairs(v) do
|
||||
if k ~= "name" and k ~= "package" and k:sub(1,1) ~= "." then
|
||||
if k == "depends" then
|
||||
s.depends = self:_read_dependency( v2, s.depends )
|
||||
if not s.depends then
|
||||
return false, scheme:error(
|
||||
ERR.SME_BADDEP(so, luci.util.serialize_data(s.depends))
|
||||
)
|
||||
end
|
||||
elseif k == "dynamic" or k == "unique" or
|
||||
k == "required" or k == "named"
|
||||
then
|
||||
s[k] = _bool(v2)
|
||||
else
|
||||
s[k] = v2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Step 1: get all sections
|
||||
for i, conf in ipairs( schemes ) do
|
||||
for k, v in pairs( conf ) do
|
||||
if v['.type'] == 'section' then
|
||||
s.dynamic = s.dynamic or false
|
||||
s.unique = s.unique or false
|
||||
s.required = s.required or false
|
||||
s.named = s.named or false
|
||||
end
|
||||
|
||||
ok, err = _req( TYPE_SECTION, k, v, { "name", "package" } )
|
||||
if err then return false, scheme:error(err) end
|
||||
|
||||
local r, err = _ref( TYPE_SECTION, v )
|
||||
if err then return false, scheme:error(err) end
|
||||
|
||||
self.packages[r[1]] =
|
||||
self.packages[r[1]] or {
|
||||
["name"] = r[1];
|
||||
["sections"] = { };
|
||||
["variables"] = { };
|
||||
}
|
||||
|
||||
local p = self.packages[r[1]]
|
||||
p.sections[v.name] = p.sections[v.name] or { }
|
||||
p.variables[v.name] = p.variables[v.name] or { }
|
||||
|
||||
local s = p.sections[v.name]
|
||||
local so = scheme:section(v.name)
|
||||
|
||||
for k, v2 in pairs(v) do
|
||||
if k ~= "name" and k ~= "package" and k:sub(1,1) ~= "." then
|
||||
if k == "depends" then
|
||||
s.depends = self:_read_dependency( v2, s.depends )
|
||||
if not s.depends then
|
||||
return false, scheme:error(
|
||||
ERR.SME_BADDEP(so, luci.util.serialize_data(s.depends))
|
||||
)
|
||||
end
|
||||
elseif k == "dynamic" or k == "unique" or
|
||||
k == "required" or k == "named"
|
||||
then
|
||||
s[k] = _bool(v2)
|
||||
else
|
||||
s[k] = v2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
s.dynamic = s.dynamic or false
|
||||
s.unique = s.unique or false
|
||||
s.required = s.required or false
|
||||
s.named = s.named or false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Step 2: get all variables
|
||||
for i, conf in ipairs( schemes ) do
|
||||
for k, v in pairs( conf ) do
|
||||
if v['.type'] == "variable" then
|
||||
function UVL._parse_var(self, scheme, k, v)
|
||||
local ok, err = _req( TYPE_OPTION, k, v, { "name", "section" } )
|
||||
if err then error(scheme:error(err)) end
|
||||
|
||||
ok, err = _req( TYPE_OPTION, k, v, { "name", "section" } )
|
||||
if err then return false, scheme:error(err) end
|
||||
local r, err = _ref( TYPE_OPTION, v )
|
||||
if err then error(scheme:error(err)) end
|
||||
|
||||
local r, err = _ref( TYPE_OPTION, v )
|
||||
if err then return false, scheme:error(err) end
|
||||
local p = self.packages[r[1]]
|
||||
if not p then
|
||||
error(scheme:error(
|
||||
ERR.SME_VBADPACK({scheme:sid(), '', v.name}, r[1])
|
||||
))
|
||||
end
|
||||
|
||||
local p = self.packages[r[1]]
|
||||
if not p then
|
||||
return false, scheme:error(
|
||||
ERR.SME_VBADPACK({scheme:sid(), '', v.name}, r[1])
|
||||
)
|
||||
local s = p.variables[r[2]]
|
||||
if not s then
|
||||
error(scheme:error(
|
||||
ERR.SME_VBADSECT({scheme:sid(), '', v.name}, r[2])
|
||||
))
|
||||
end
|
||||
|
||||
s[v.name] = s[v.name] or { }
|
||||
|
||||
local t = s[v.name]
|
||||
local so = scheme:section(r[2])
|
||||
local to = so:option(v.name)
|
||||
|
||||
for k, v2 in pairs(v) do
|
||||
if k ~= "name" and k ~= "section" and k:sub(1,1) ~= "." then
|
||||
if k == "depends" then
|
||||
t.depends = self:_read_dependency( v2, t.depends )
|
||||
if not t.depends then
|
||||
error(scheme:error(so:error(
|
||||
ERR.SME_BADDEP(to, luci.util.serialize_data(v2))
|
||||
)))
|
||||
end
|
||||
|
||||
local s = p.variables[r[2]]
|
||||
if not s then
|
||||
return false, scheme:error(
|
||||
ERR.SME_VBADSECT({scheme:sid(), '', v.name}, r[2])
|
||||
)
|
||||
elseif k == "validator" then
|
||||
t.validators = self:_read_validator( v2, t.validators )
|
||||
if not t.validators then
|
||||
error(scheme:error(so:error(
|
||||
ERR.SME_BADVAL(to, luci.util.serialize_data(v2))
|
||||
)))
|
||||
end
|
||||
|
||||
s[v.name] = s[v.name] or { }
|
||||
|
||||
local t = s[v.name]
|
||||
local so = scheme:section(r[2])
|
||||
local to = so:option(v.name)
|
||||
|
||||
for k, v2 in pairs(v) do
|
||||
if k ~= "name" and k ~= "section" and k:sub(1,1) ~= "." then
|
||||
if k == "depends" then
|
||||
t.depends = self:_read_dependency( v2, t.depends )
|
||||
if not t.depends then
|
||||
return false, scheme:error(so:error(
|
||||
ERR.SME_BADDEP(to, luci.util.serialize_data(v2))
|
||||
))
|
||||
end
|
||||
elseif k == "validator" then
|
||||
t.validators = self:_read_validator( v2, t.validators )
|
||||
if not t.validators then
|
||||
return false, scheme:error(so:error(
|
||||
ERR.SME_BADVAL(to, luci.util.serialize_data(v2))
|
||||
))
|
||||
end
|
||||
elseif k == "valueof" then
|
||||
local values, err = self:_read_reference( v2 )
|
||||
if err then
|
||||
return false, scheme:error(so:error(
|
||||
ERR.REFERENCE(to, luci.util.serialize_data(v2)):child(err)
|
||||
))
|
||||
end
|
||||
t.type = "reference"
|
||||
t.values = values
|
||||
elseif k == "required" then
|
||||
t[k] = _bool(v2)
|
||||
else
|
||||
t[k] = t[k] or v2
|
||||
end
|
||||
end
|
||||
elseif k == "valueof" then
|
||||
local values, err = self:_read_reference( v2 )
|
||||
if err then
|
||||
error(scheme:error(so:error(
|
||||
ERR.REFERENCE(to, luci.util.serialize_data(v2)):child(err)
|
||||
)))
|
||||
end
|
||||
|
||||
t.type = t.type or "variable"
|
||||
t.datatype = t.datatype or "string"
|
||||
t.required = t.required or false
|
||||
t.type = "reference"
|
||||
t.values = values
|
||||
elseif k == "required" then
|
||||
t[k] = _bool(v2)
|
||||
else
|
||||
t[k] = t[k] or v2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Step 3: get all enums
|
||||
for i, conf in ipairs( schemes ) do
|
||||
for k, v in pairs( conf ) do
|
||||
if v['.type'] == "enum" then
|
||||
t.type = t.type or "variable"
|
||||
t.datatype = t.datatype or "string"
|
||||
t.required = t.required or false
|
||||
end
|
||||
|
||||
ok, err = _req( TYPE_ENUM, k, v, { "value", "variable" } )
|
||||
if err then return false, scheme:error(err) end
|
||||
-- Step 3: get all enums
|
||||
function UVL._parse_enum(self, scheme, k, v)
|
||||
local ok, err = _req( TYPE_ENUM, k, v, { "value", "variable" } )
|
||||
if err then error(scheme:error(err)) end
|
||||
|
||||
local r, err = _ref( TYPE_ENUM, v )
|
||||
if err then return false, scheme:error(err) end
|
||||
local r, err = _ref( TYPE_ENUM, v )
|
||||
if err then error(scheme:error(err)) end
|
||||
|
||||
local p = self.packages[r[1]]
|
||||
if not p then
|
||||
return false, scheme:error(
|
||||
ERR.SME_EBADPACK({scheme:sid(), '', '', v.value}, r[1])
|
||||
)
|
||||
end
|
||||
|
||||
local s = p.variables[r[2]]
|
||||
if not s then
|
||||
return false, scheme:error(
|
||||
ERR.SME_EBADSECT({scheme:sid(), '', '', v.value}, r[2])
|
||||
)
|
||||
end
|
||||
|
||||
local t = s[r[3]]
|
||||
if not t then
|
||||
return false, scheme:error(
|
||||
ERR.SME_EBADOPT({scheme:sid(), '', '', v.value}, r[3])
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
local so = scheme:section(r[2])
|
||||
local oo = so:option(r[3])
|
||||
local eo = oo:enum(v.value)
|
||||
|
||||
if t.type ~= "enum" and t.type ~= "reference" then
|
||||
return false, scheme:error(ERR.SME_EBADTYPE(eo))
|
||||
end
|
||||
|
||||
if not t.values then
|
||||
t.values = { [v.value] = v.title or v.value }
|
||||
else
|
||||
t.values[v.value] = v.title or v.value
|
||||
end
|
||||
|
||||
if not t.enum_depends then
|
||||
t.enum_depends = { }
|
||||
end
|
||||
|
||||
if v.default then
|
||||
if t.default then
|
||||
return false, scheme:error(ERR.SME_EBADDEF(eo))
|
||||
end
|
||||
t.default = v.value
|
||||
end
|
||||
|
||||
if v.depends then
|
||||
t.enum_depends[v.value] = self:_read_dependency(
|
||||
v.depends, t.enum_depends[v.value]
|
||||
)
|
||||
|
||||
if not t.enum_depends[v.value] then
|
||||
return false, scheme:error(so:error(oo:error(
|
||||
ERR.SME_BADDEP(eo, luci.util.serialize_data(v.depends))
|
||||
)))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local p = self.packages[r[1]]
|
||||
if not p then
|
||||
error(scheme:error(
|
||||
ERR.SME_EBADPACK({scheme:sid(), '', '', v.value}, r[1])
|
||||
))
|
||||
end
|
||||
|
||||
return self
|
||||
local s = p.variables[r[2]]
|
||||
if not s then
|
||||
error(scheme:error(
|
||||
ERR.SME_EBADSECT({scheme:sid(), '', '', v.value}, r[2])
|
||||
))
|
||||
end
|
||||
|
||||
local t = s[r[3]]
|
||||
if not t then
|
||||
error(scheme:error(
|
||||
ERR.SME_EBADOPT({scheme:sid(), '', '', v.value}, r[3])
|
||||
))
|
||||
end
|
||||
|
||||
|
||||
local so = scheme:section(r[2])
|
||||
local oo = so:option(r[3])
|
||||
local eo = oo:enum(v.value)
|
||||
|
||||
if t.type ~= "enum" and t.type ~= "reference" then
|
||||
error(scheme:error(ERR.SME_EBADTYPE(eo)))
|
||||
end
|
||||
|
||||
if not t.values then
|
||||
t.values = { [v.value] = v.title or v.value }
|
||||
else
|
||||
t.values[v.value] = v.title or v.value
|
||||
end
|
||||
|
||||
if not t.enum_depends then
|
||||
t.enum_depends = { }
|
||||
end
|
||||
|
||||
if v.default then
|
||||
if t.default then
|
||||
error(scheme:error(ERR.SME_EBADDEF(eo)))
|
||||
end
|
||||
t.default = v.value
|
||||
end
|
||||
|
||||
if v.depends then
|
||||
t.enum_depends[v.value] = self:_read_dependency(
|
||||
v.depends, t.enum_depends[v.value]
|
||||
)
|
||||
|
||||
if not t.enum_depends[v.value] then
|
||||
error(scheme:error(so:error(oo:error(
|
||||
ERR.SME_BADDEP(eo, luci.util.serialize_data(v.depends))
|
||||
))))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Read a dependency specification
|
||||
|
|
Loading…
Reference in a new issue