mirror of
https://github.com/bjc/prosody.git
synced 2025-04-01 20:27:39 +03:00
353 lines
9.3 KiB
Lua
353 lines
9.3 KiB
Lua
-- This file is generated from teal-src/util/datamapper.lua
|
|
|
|
if not math.type then
|
|
require("prosody.util.mathcompat")
|
|
end
|
|
|
|
local st = require("prosody.util.stanza");
|
|
local pointer = require("prosody.util.jsonpointer");
|
|
|
|
local schema_t = {}
|
|
|
|
local function toboolean(s)
|
|
if s == "true" or s == "1" then
|
|
return true
|
|
elseif s == "false" or s == "0" then
|
|
return false
|
|
elseif s then
|
|
return true
|
|
end
|
|
end
|
|
|
|
local function totype(t, s)
|
|
if not s then
|
|
return nil
|
|
end
|
|
if t == "string" then
|
|
return s
|
|
elseif t == "boolean" then
|
|
return toboolean(s)
|
|
elseif t == "number" or t == "integer" then
|
|
return tonumber(s)
|
|
end
|
|
end
|
|
|
|
local value_goes = {}
|
|
|
|
local function resolve_schema(schema, root)
|
|
if type(schema) == "table" then
|
|
if schema["$ref"] and schema["$ref"]:sub(1, 1) == "#" then
|
|
return pointer.resolve(root, schema["$ref"]:sub(2))
|
|
end
|
|
end
|
|
return schema
|
|
end
|
|
|
|
local function guess_schema_type(schema)
|
|
local schema_types = schema.type
|
|
if type(schema_types) == "string" then
|
|
return schema_types
|
|
elseif schema_types ~= nil then
|
|
error("schema has unsupported 'type' property")
|
|
elseif schema.properties then
|
|
return "object"
|
|
elseif schema.items then
|
|
return "array"
|
|
end
|
|
return "string"
|
|
end
|
|
|
|
local function unpack_propschema(propschema, propname, current_ns)
|
|
|
|
local proptype = "string"
|
|
local value_where = propname and "in_text_tag" or "in_text"
|
|
local name = propname
|
|
local namespace
|
|
local prefix
|
|
local single_attribute
|
|
local enums
|
|
|
|
if type(propschema) == "table" then
|
|
proptype = guess_schema_type(propschema);
|
|
elseif type(propschema) == "string" then
|
|
error("schema as string is not supported: " .. propschema .. " {" .. current_ns .. "}" .. propname)
|
|
end
|
|
|
|
if proptype == "object" or proptype == "array" then
|
|
value_where = "in_children"
|
|
end
|
|
|
|
if type(propschema) == "table" then
|
|
local xml = propschema.xml
|
|
if xml then
|
|
if xml.name then
|
|
name = xml.name
|
|
end
|
|
if xml.namespace and xml.namespace ~= current_ns then
|
|
namespace = xml.namespace
|
|
end
|
|
if xml.prefix then
|
|
prefix = xml.prefix
|
|
end
|
|
if proptype == "array" and xml.wrapped then
|
|
value_where = "in_wrapper"
|
|
elseif xml.attribute then
|
|
value_where = "in_attribute"
|
|
elseif xml.text then
|
|
value_where = "in_text"
|
|
elseif xml.x_name_is_value then
|
|
value_where = "in_tag_name"
|
|
elseif xml.x_single_attribute then
|
|
single_attribute = xml.x_single_attribute
|
|
value_where = "in_single_attribute"
|
|
end
|
|
end
|
|
if propschema["const"] then
|
|
enums = {propschema["const"]}
|
|
elseif propschema["enum"] then
|
|
enums = propschema["enum"]
|
|
end
|
|
end
|
|
|
|
return proptype, value_where, name, namespace, prefix, single_attribute, enums
|
|
end
|
|
|
|
local parse_object
|
|
local parse_array
|
|
|
|
local function extract_value(s, value_where, proptype, name, namespace, prefix, single_attribute, enums)
|
|
if value_where == "in_tag_name" then
|
|
local c
|
|
if proptype == "boolean" then
|
|
c = s:get_child(name, namespace);
|
|
elseif enums and proptype == "string" then
|
|
|
|
for i = 1, #enums do
|
|
c = s:get_child(enums[i], namespace);
|
|
if c then
|
|
break
|
|
end
|
|
end
|
|
else
|
|
c = s:get_child(nil, namespace);
|
|
end
|
|
if c then
|
|
return c.name
|
|
end
|
|
elseif value_where == "in_attribute" then
|
|
local attr = name
|
|
if prefix then
|
|
attr = prefix .. ":" .. name
|
|
elseif namespace and namespace ~= s.attr.xmlns then
|
|
attr = namespace .. "\1" .. name
|
|
end
|
|
return s.attr[attr]
|
|
|
|
elseif value_where == "in_text" then
|
|
return s:get_text()
|
|
|
|
elseif value_where == "in_single_attribute" then
|
|
local c = s:get_child(name, namespace)
|
|
return c and c.attr[single_attribute]
|
|
elseif value_where == "in_text_tag" then
|
|
return s:get_child_text(name, namespace)
|
|
end
|
|
end
|
|
|
|
function parse_object(schema, s, root)
|
|
local out = {}
|
|
schema = resolve_schema(schema, root)
|
|
if type(schema) == "table" and schema.properties then
|
|
for prop, propschema in pairs(schema.properties) do
|
|
propschema = resolve_schema(propschema, root)
|
|
|
|
local proptype, value_where, name, namespace, prefix, single_attribute, enums = unpack_propschema(propschema, prop, s.attr.xmlns)
|
|
|
|
if value_where == "in_children" and type(propschema) == "table" then
|
|
if proptype == "object" then
|
|
local c = s:get_child(name, namespace)
|
|
if c then
|
|
out[prop] = parse_object(propschema, c, root);
|
|
end
|
|
elseif proptype == "array" then
|
|
local a = parse_array(propschema, s, root);
|
|
if a and a[1] ~= nil then
|
|
out[prop] = a;
|
|
end
|
|
else
|
|
error("unreachable")
|
|
end
|
|
elseif value_where == "in_wrapper" and type(propschema) == "table" and proptype == "array" then
|
|
local wrapper = s:get_child(name, namespace);
|
|
if wrapper then
|
|
out[prop] = parse_array(propschema, wrapper, root);
|
|
end
|
|
else
|
|
local value = extract_value(s, value_where, proptype, name, namespace, prefix, single_attribute, enums)
|
|
|
|
out[prop] = totype(proptype, value)
|
|
end
|
|
end
|
|
end
|
|
|
|
return out
|
|
end
|
|
|
|
function parse_array(schema, s, root)
|
|
local itemschema = resolve_schema(schema.items, root);
|
|
local proptype, value_where, child_name, namespace, prefix, single_attribute, enums = unpack_propschema(itemschema, nil, s.attr.xmlns)
|
|
local attr_name
|
|
if value_where == "in_single_attribute" then
|
|
value_where = "in_attribute";
|
|
attr_name = single_attribute;
|
|
end
|
|
local out = {}
|
|
|
|
if proptype == "object" then
|
|
if type(itemschema) == "table" then
|
|
for c in s:childtags(child_name, namespace) do
|
|
table.insert(out, parse_object(itemschema, c, root));
|
|
end
|
|
else
|
|
error("array items must be schema object")
|
|
end
|
|
elseif proptype == "array" then
|
|
if type(itemschema) == "table" then
|
|
for c in s:childtags(child_name, namespace) do
|
|
table.insert(out, parse_array(itemschema, c, root));
|
|
end
|
|
end
|
|
else
|
|
for c in s:childtags(child_name, namespace) do
|
|
local value = extract_value(c, value_where, proptype, attr_name or child_name, namespace, prefix, single_attribute, enums)
|
|
|
|
table.insert(out, totype(proptype, value));
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
local function parse(schema, s)
|
|
local s_type = guess_schema_type(schema)
|
|
if s_type == "object" then
|
|
return parse_object(schema, s, schema)
|
|
elseif s_type == "array" then
|
|
return parse_array(schema, s, schema)
|
|
else
|
|
error("top-level scalars unsupported")
|
|
end
|
|
end
|
|
|
|
local function toxmlstring(proptype, v)
|
|
if proptype == "string" and type(v) == "string" then
|
|
return v
|
|
elseif proptype == "number" and type(v) == "number" then
|
|
return string.format("%g", v)
|
|
elseif proptype == "integer" and type(v) == "number" then
|
|
return string.format("%d", v)
|
|
elseif proptype == "boolean" then
|
|
return v and "1" or "0"
|
|
end
|
|
end
|
|
|
|
local unparse
|
|
|
|
local function unparse_property(out, v, proptype, propschema, value_where, name, namespace, current_ns, prefix,
|
|
single_attribute, root)
|
|
|
|
if value_where == "in_attribute" then
|
|
local attr = name
|
|
if prefix then
|
|
attr = prefix .. ":" .. name
|
|
elseif namespace and namespace ~= current_ns then
|
|
attr = namespace .. "\1" .. name
|
|
end
|
|
|
|
out.attr[attr] = toxmlstring(proptype, v)
|
|
elseif value_where == "in_text" then
|
|
out:text(toxmlstring(proptype, v))
|
|
elseif value_where == "in_single_attribute" then
|
|
assert(single_attribute)
|
|
local propattr = {}
|
|
|
|
if namespace and namespace ~= current_ns then
|
|
propattr.xmlns = namespace
|
|
end
|
|
|
|
propattr[single_attribute] = toxmlstring(proptype, v)
|
|
out:tag(name, propattr):up();
|
|
|
|
else
|
|
local propattr
|
|
if namespace ~= current_ns then
|
|
propattr = {xmlns = namespace}
|
|
end
|
|
if value_where == "in_tag_name" then
|
|
if proptype == "string" and type(v) == "string" then
|
|
out:tag(v, propattr):up();
|
|
elseif proptype == "boolean" and v == true then
|
|
out:tag(name, propattr):up();
|
|
end
|
|
elseif proptype == "object" and type(propschema) == "table" and type(v) == "table" then
|
|
local c = unparse(propschema, v, name, namespace, nil, root);
|
|
if c then
|
|
out:add_direct_child(c);
|
|
end
|
|
elseif proptype == "array" and type(propschema) == "table" and type(v) == "table" then
|
|
if value_where == "in_wrapper" then
|
|
local c = unparse(propschema, v, name, namespace, nil, root);
|
|
if c then
|
|
out:add_direct_child(c);
|
|
end
|
|
else
|
|
unparse(propschema, v, name, namespace, out, root);
|
|
end
|
|
else
|
|
out:text_tag(name, toxmlstring(proptype, v), propattr)
|
|
end
|
|
end
|
|
end
|
|
|
|
function unparse(schema, t, current_name, current_ns, ctx, root)
|
|
|
|
if root == nil then
|
|
root = schema
|
|
end
|
|
|
|
if schema.xml then
|
|
if schema.xml.name then
|
|
current_name = schema.xml.name
|
|
end
|
|
if schema.xml.namespace then
|
|
current_ns = schema.xml.namespace
|
|
end
|
|
|
|
end
|
|
|
|
local out = ctx or st.stanza(current_name, {xmlns = current_ns})
|
|
|
|
local s_type = guess_schema_type(schema)
|
|
if s_type == "object" then
|
|
|
|
for prop, propschema in pairs(schema.properties) do
|
|
propschema = resolve_schema(propschema, root)
|
|
local v = t[prop]
|
|
|
|
if v ~= nil then
|
|
local proptype, value_where, name, namespace, prefix, single_attribute = unpack_propschema(propschema, prop, current_ns)
|
|
unparse_property(out, v, proptype, propschema, value_where, name, namespace, current_ns, prefix, single_attribute, root)
|
|
end
|
|
end
|
|
return out
|
|
|
|
elseif s_type == "array" then
|
|
local itemschema = resolve_schema(schema.items, root)
|
|
local proptype, value_where, name, namespace, prefix, single_attribute = unpack_propschema(itemschema, current_name, current_ns)
|
|
for _, item in ipairs(t) do
|
|
unparse_property(out, item, proptype, itemschema, value_where, name, namespace, current_ns, prefix, single_attribute, root)
|
|
end
|
|
return out
|
|
end
|
|
end
|
|
|
|
return {parse = parse; unparse = unparse}
|