Software /
code /
prosody
Diff
util/xtemplate.lua @ 12213:dc9d63166488
util.xtemplate: Yet another string template library
This one takes a stanza as input
Roughly based on util.interpolation
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Mon, 24 Jan 2022 23:54:32 +0100 |
child | 12975:d10957394a3c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/xtemplate.lua Mon Jan 24 23:54:32 2022 +0100 @@ -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 }