return ltype:match('^(.*)_list$')
end
+-- Indexing values of arbitrary types can raise (e.g. numbers), so it is done via pcall
+local function safe_index(obj, key)
+ return obj[key]
+end
+
local function implicit_tostring(t, ud_or_table)
if t == 'table' then
-- Table (very special)
-- Plain table field
ret = inp[method_name]
else
+ local ok, meth = pcall(safe_index, inp, method_name)
+
+ if not ok or type(meth) ~= 'function' then
+ if transform_function[method_name] then
+ logger.errx('cannot call method `%s` on %s value; use `.%s` to apply the transform of the same name',
+ method_name, t, method_name)
+ else
+ logger.errx('cannot call method `%s` on %s value', method_name, t)
+ end
+ return nil
+ end
+
-- We call method unpacking arguments and dropping all but the first result returned
- ret = (inp[method_name](inp, unpack_function(args or E)))
+ ret = (meth(inp, unpack_function(args or E)))
end
local ret_type = type(ret)
assert_rspamd_table_eq_sorted({actual = elts, expect = case.expect})
end)
end
+
+ -- Method syntax (`:name`) on a value that has no such method must yield
+ -- nothing instead of raising a runtime error
+ local cases_nil = {
+ ["transform used as method"] = {
+ selector = "time:digest",
+ },
+ ["missing method on string"] = {
+ selector = "header('Subject'):totally_missing_method(1)",
+ },
+ }
+ for case_name, case in lua_util.spairs(cases_nil) do
+ test("nil case " .. case_name, function()
+ local sels = lua_selectors.create_selector_closure_fn(nil, cfg, case.selector, nil,
+ function(_, res, _) return res end)
+ assert_not_nil(sels)
+ local ok, res = pcall(sels, task)
+ assert_true(ok)
+ assert_nil(res)
+ end)
+ end
end)