File

util/interpolation.lua @ 11245:43b43e7156b8

MUC: Add support for presence probes (fixes #1535) The following patch allows Prosody to respond to `probe` presences and send out the probed occupant's current presence. This is based on line 17.3 in XEP-0045: A MUC service MAY handle presence probes sent to the room JID <room@service> or an occupant JID <room@service/nick> (e.g, these might be sent by an occupant's home server to determine if the room is still online or to synchronize presence information if the user or the user's server has gone offline temporarily or has started sharing presence again, as for instance when Stanza Interception and Filtering Technology (XEP-0273) is used).
author JC Brand <jc@opkode.com>
date Sun, 19 Apr 2020 21:49:45 +0200
parent 11064:af1e3b7d9ea3
child 11307:f2e276bb4ef8
line wrap: on
line source

-- Simple template language
--
-- The new() function takes a pattern and an escape function and returns
-- a render() function.  Both are required.
--
-- The function render() takes a string template and a table of values.
-- Sequences like {name} in the template string are substituted
-- with values from the table, optionally depending on a modifier
-- symbol.
--
-- Variants are:
-- {name} is substituted for values["name"] and is escaped using the
-- second argument to new_render().  To disable the escaping, use {name!}.
-- {name.item} can be used to access table items.
-- To renter lists of items: {name# item number {idx} is {item} }
-- Or key-value pairs: {name% t[ {idx} ] = {item} }
-- To show a defaults for missing values {name? sub-template } can be used,
-- which renders a sub-template if values["name"] is false-ish.
-- {name& sub-template } does the opposite, the sub-template is rendered
-- if the selected value is anything but false or nil.

local type, tostring = type, tostring;
local pairs, ipairs = pairs, ipairs;
local s_sub, s_gsub, s_match = string.sub, string.gsub, string.match;
local t_concat = table.concat;

local function new_render(pat, escape, funcs)
	-- assert(type(pat) == "string", "bad argument #1 to 'new_render' (string expected)");
	-- assert(type(escape) == "function", "bad argument #2 to 'new_render' (function expected)");
	local function render(template, values)
		-- assert(type(template) == "string", "bad argument #1 to 'render' (string expected)");
		-- assert(type(values) == "table", "bad argument #2 to 'render' (table expected)");
		return (s_gsub(template, pat, function (block)
			block = s_sub(block, 2, -2);
			local name, raw, opt, e = s_match(block, "^([%a_][%w_.]*)(!?)(%p?)()");
			if not name then return end
			local value = values[name];
			if not value and name:find(".", 2, true) then
				value = values;
				for word in name:gmatch"[^.]+" do
					value = value[word];
					if not value then break; end
				end
			end
			if funcs then
				while value ~= nil and opt == '|' do
					local f;
					f, raw, opt, e = s_match(block, "^([%a_][%w_.]*)(!?)(%p?)()", e);
					f = funcs[f];
					if f then value = f(value); end
				end
			end
			if opt == '#' or opt == '%' then
				if type(value) ~= "table" then return ""; end
				local iter = opt == '#' and ipairs or pairs;
				local out, i, subtpl = {}, 1, s_sub(block, e);
				local subvalues = setmetatable({}, { __index = values });
				for idx, item in iter(value) do
					subvalues.idx = idx;
					subvalues.item = item;
					out[i], i = render(subtpl, subvalues), i+1;
				end
				return t_concat(out);
			elseif opt == '&' then
				if not value then return ""; end
				return render(s_sub(block, e), values);
			elseif opt == '~' then
				if value then return ""; end
				return render(s_sub(block, e), values);
			elseif opt == '?' and not value then
				return render(s_sub(block, e), values);
			elseif value ~= nil then
				if type(value) ~= "string" then
					value = tostring(value);
				end
				if raw ~= '!' then
					return escape(value);
				end
				return value;
			end
		end));
	end
	return render;
end

return {
	new = new_render;
};