Comparison

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
comparison
equal deleted inserted replaced
12212:bc6fc1cb04ae 12213:dc9d63166488
1 local s_gsub = string.gsub;
2 local s_match = string.match;
3 local s_sub = string.sub;
4 local t_concat = table.concat;
5
6 local st = require("util.stanza");
7
8 local function render(template, root, escape, filters)
9 escape = escape or st.xml_escape;
10
11 return (s_gsub(template, "%b{}", function(block)
12 local inner = s_sub(block, 2, -2);
13 local path, pipe, pos = s_match(inner, "^([^|]+)(|?)()");
14 if not (type(path) == "string") then return end
15 local value
16 if path == "." then
17 value = root;
18 elseif path == "#" then
19 value = root:get_text();
20 else
21 value = root:find(path);
22 end
23 local is_escaped = false;
24
25 while pipe == "|" do
26 local func, args, tmpl, p = s_match(inner, "^(%w+)(%b())(%b{})()", pos);
27 if not func then func, args, p = s_match(inner, "^(%w+)(%b())()", pos); end
28 if not func then func, tmpl, p = s_match(inner, "^(%w+)(%b{})()", pos); end
29 if not func then func, p = s_match(inner, "^(%w+)()", pos); end
30 if not func then break end
31 if tmpl then tmpl = s_sub(tmpl, 2, -2); end
32 if args then args = s_sub(args, 2, -2); end
33
34 if func == "each" and tmpl and st.is_stanza(value) then
35 if not args then value, args = root, path; end
36 local ns, name = s_match(args, "^(%b{})(.*)$");
37 if ns then
38 ns = s_sub(ns, 2, -2);
39 else
40 name, ns = args, nil;
41 end
42 if ns == "" then ns = nil; end
43 if name == "" then name = nil; end
44 local out, i = {}, 1;
45 for c in (value):childtags(name, ns) do out[i], i = render(tmpl, c, escape, filters), i + 1; end
46 value = t_concat(out);
47 is_escaped = true;
48 elseif func == "and" and tmpl then
49 local condition = value;
50 if args then condition = root:find(args); end
51 if condition then
52 value = render(tmpl, root, escape, filters);
53 is_escaped = true;
54 end
55 elseif func == "or" and tmpl then
56 local condition = value;
57 if args then condition = root:find(args); end
58 if not condition then
59 value = render(tmpl, root, escape, filters);
60 is_escaped = true;
61 end
62 elseif filters and filters[func] then
63 local f = filters[func];
64 if args == nil then
65 value, is_escaped = f(value, tmpl);
66 else
67 value, is_escaped = f(args, value, tmpl);
68 end
69 else
70 error("No such filter function: " .. func);
71 end
72 pipe, pos = s_match(inner, "^(|?)()", p);
73 end
74
75 if type(value) == "string" then
76 if not is_escaped then value = escape(value); end
77 return value
78 elseif st.is_stanza(value) then
79 value = value:get_text();
80 if value then return escape(value) end
81 end
82 return ""
83 end))
84 end
85
86 return { render = render }