Software /
code /
prosody
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 } |