aboutsummaryrefslogtreecommitdiffstats
path: root/util
diff options
context:
space:
mode:
authorKim Alvefur <zash@zash.se>2022-01-24 23:54:32 +0100
committerKim Alvefur <zash@zash.se>2022-01-24 23:54:32 +0100
commit7e65b1deef086d12cff6a129857a8ba335b4eccf (patch)
treea6a85a91e784247d2a1250448ddf9a1e4a0fc2a4 /util
parent55da054c9b111f4543ad87d62f252e4aec95fcfb (diff)
downloadprosody-7e65b1deef086d12cff6a129857a8ba335b4eccf.tar.gz
prosody-7e65b1deef086d12cff6a129857a8ba335b4eccf.zip
util.xtemplate: Yet another string template library
This one takes a stanza as input Roughly based on util.interpolation
Diffstat (limited to 'util')
-rw-r--r--util/xtemplate.lua86
1 files changed, 86 insertions, 0 deletions
diff --git a/util/xtemplate.lua b/util/xtemplate.lua
new file mode 100644
index 00000000..254c8af0
--- /dev/null
+++ b/util/xtemplate.lua
@@ -0,0 +1,86 @@
+local s_gsub = string.gsub;
+local s_match = string.match;
+local s_sub = string.sub;
+local t_concat = table.concat;
+
+local st = require("util.stanza");
+
+local function render(template, root, escape, filters)
+ escape = escape or st.xml_escape;
+
+ return (s_gsub(template, "%b{}", function(block)
+ local inner = s_sub(block, 2, -2);
+ local path, pipe, pos = s_match(inner, "^([^|]+)(|?)()");
+ if not (type(path) == "string") then return end
+ local value
+ if path == "." then
+ value = root;
+ elseif path == "#" then
+ value = root:get_text();
+ else
+ value = root:find(path);
+ end
+ local is_escaped = false;
+
+ while pipe == "|" do
+ local func, args, tmpl, p = s_match(inner, "^(%w+)(%b())(%b{})()", pos);
+ if not func then func, args, p = s_match(inner, "^(%w+)(%b())()", pos); end
+ if not func then func, tmpl, p = s_match(inner, "^(%w+)(%b{})()", pos); end
+ if not func then func, p = s_match(inner, "^(%w+)()", pos); end
+ if not func then break end
+ if tmpl then tmpl = s_sub(tmpl, 2, -2); end
+ if args then args = s_sub(args, 2, -2); end
+
+ if func == "each" and tmpl and st.is_stanza(value) then
+ if not args then value, args = root, path; end
+ local ns, name = s_match(args, "^(%b{})(.*)$");
+ if ns then
+ ns = s_sub(ns, 2, -2);
+ else
+ name, ns = args, nil;
+ end
+ if ns == "" then ns = nil; end
+ if name == "" then name = nil; end
+ local out, i = {}, 1;
+ for c in (value):childtags(name, ns) do out[i], i = render(tmpl, c, escape, filters), i + 1; end
+ value = t_concat(out);
+ is_escaped = true;
+ elseif func == "and" and tmpl then
+ local condition = value;
+ if args then condition = root:find(args); end
+ if condition then
+ value = render(tmpl, root, escape, filters);
+ is_escaped = true;
+ end
+ elseif func == "or" and tmpl then
+ local condition = value;
+ if args then condition = root:find(args); end
+ if not condition then
+ value = render(tmpl, root, escape, filters);
+ is_escaped = true;
+ end
+ elseif filters and filters[func] then
+ local f = filters[func];
+ if args == nil then
+ value, is_escaped = f(value, tmpl);
+ else
+ value, is_escaped = f(args, value, tmpl);
+ end
+ else
+ error("No such filter function: " .. func);
+ end
+ pipe, pos = s_match(inner, "^(|?)()", p);
+ end
+
+ if type(value) == "string" then
+ if not is_escaped then value = escape(value); end
+ return value
+ elseif st.is_stanza(value) then
+ value = value:get_text();
+ if value then return escape(value) end
+ end
+ return ""
+ end))
+end
+
+return { render = render }