1 -- luacheck: ignore 213/i
2 local stanza_mt = require "util.stanza".stanza_mt;
3 local setmetatable = setmetatable;
7 local loadstring = loadstring;
9 local t_remove = table.remove;
10 local parse_xml = require "util.xml".parse;
14 local function trim_xml(stanza)
16 local child = stanza[i];
20 child = child:gsub("^%s*", ""):gsub("%s*$", "");
22 if child == "" then t_remove(stanza, i); end
27 local function create_string_string(str)
28 str = ("%q"):format(str);
29 str = str:gsub("{([^}]*)}", function(s)
30 return '"..(data["'..s..'"]or"").."';
34 local function create_attr_string(attr, xmlns)
36 for name,value in pairs(attr) do
37 if name ~= "xmlns" or value ~= xmlns then
38 str = str..("[%q]=%s;"):format(name, create_string_string(value));
43 local function create_clone_string(stanza, lookup, xmlns)
44 if not lookup[stanza] then
45 local s = ('setmetatable({name=%q,attr=%s,tags={'):format(stanza.name, create_attr_string(stanza.attr, xmlns));
47 for i,tag in ipairs(stanza.tags) do
48 s = s..create_clone_string(tag, lookup, stanza.attr.xmlns)..";";
52 for i,child in ipairs(stanza) do
54 s = s..create_clone_string(child, lookup, stanza.attr.xmlns)..";";
56 s = s..create_string_string(child)..";"
59 s = s..'}, stanza_mt)';
60 s = s:gsub('%.%.""', ""):gsub('([=;])""%.%.', "%1"):gsub(';"";', ";"); -- strip empty strings
61 local n = #lookup + 1;
63 lookup[stanza] = "_"..n;
65 return lookup[stanza];
67 local function create_cloner(stanza, chunkname)
69 local name = create_clone_string(stanza, lookup, "");
70 local src = "local setmetatable,stanza_mt=...;return function(data)";
72 src = src.."local _"..i.."="..lookup[i]..";";
74 src = src.."return "..name..";end";
75 local f,err = loadstring(src, chunkname);
76 if not f then error(err); end
77 return f(setmetatable, stanza_mt);
80 local template_mt = { __tostring = function(t) return t.name end };
81 local function create_template(templates, text)
82 local stanza, err = parse_xml(text);
83 if not stanza then error(err); end
86 local info = debug.getinfo(3, "Sl");
87 info = info and ("template(%s:%d)"):format(info.short_src:match("[^\\/]*$"), info.currentline) or "template(unknown)";
89 local template = setmetatable({ apply = create_cloner(stanza, info), name = info, text = text }, template_mt);
90 templates[text] = template;
94 local templates = setmetatable({}, { __mode = 'k', __index = create_template });
96 return templates[text];