aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@rspamd.com>2022-12-10 22:40:31 +0000
committerVsevolod Stakhov <vsevolod@rspamd.com>2022-12-10 22:40:31 +0000
commit6f8d6b19d20ce9cc42eaab7f21aa0fc6c937003a (patch)
treedde963ada32903f4affbae2e7ebfa0551b54e858
parent4dfcb9de863b6714694768a4269dcbe555d811ef (diff)
downloadrspamd-6f8d6b19d20ce9cc42eaab7f21aa0fc6c937003a.tar.gz
rspamd-6f8d6b19d20ce9cc42eaab7f21aa0fc6c937003a.zip
[Feature] Some rework of the selectors framework
* Add `apply_methods` transform * Rework userdata application of the transform functor * Add more join methods
-rw-r--r--lualib/lua_selectors/init.lua11
-rw-r--r--lualib/lua_selectors/transforms.lua48
2 files changed, 55 insertions, 4 deletions
diff --git a/lualib/lua_selectors/init.lua b/lualib/lua_selectors/init.lua
index e4d76471e..f85b9a487 100644
--- a/lualib/lua_selectors/init.lua
+++ b/lualib/lua_selectors/init.lua
@@ -97,7 +97,8 @@ local function process_selector(task, sel)
local pipe = sel.processor_pipe or E
local first_elt = pipe[1]
- if first_elt and first_elt.method then
+ if first_elt and (first_elt.method or
+ fun.any(function(t) return t == 'userdata' or t == 'table' end, first_elt.types)) then
-- Explicit conversion
local meth = first_elt
@@ -114,7 +115,7 @@ local function process_selector(task, sel)
-- Map method to a list of inputs, excluding empty elements
input = fun.filter(function(map_elt) return map_elt end,
fun.map(function(list_elt)
- local ret, _ = meth.process(list_elt, pt)
+ local ret, _ = meth.process(list_elt, pt, meth.args)
return ret
end, input))
etype = 'string_list'
@@ -124,7 +125,6 @@ local function process_selector(task, sel)
pipe = fun.drop_n(1, pipe)
elseif etype:match('^userdata') or etype:match('^table') then
-- Implicit conversion
-
local pt = pure_type(etype)
if not pt then
@@ -140,6 +140,8 @@ local function process_selector(task, sel)
end, input))
etype = 'string_list'
end
+ else
+ lua_util.debugm(M, task, 'avoid implicit conversion as the transformer accepts complex input')
end
-- Now we fold elements using left fold
@@ -189,11 +191,11 @@ local function process_selector(task, sel)
if not res or not res[1] then return nil end -- Pipeline failed
if not allowed_type(res[2]) then
-
-- Search for implicit conversion
local pt = pure_type(res[2])
if pt then
+
lua_util.debugm(M, task, 'apply implicit map %s->string_list', pt)
res[1] = fun.map(function(e) return implicit_tostring(pt, e) end, res[1])
res[2] = 'string_list'
@@ -382,6 +384,7 @@ exports.parse_selector = function(cfg, str)
local processor = lua_util.shallowcopy(transform_function[proc_name])
processor.name = proc_name
processor.args = proc_tbl[2] or E
+ logger.errx('hui: %s -> %s', proc_name, processor.args)
if not check_args(processor.name, processor.args_schema, processor.args) then
pipeline_error = 'args schema for ' .. proc_name
diff --git a/lualib/lua_selectors/transforms.lua b/lualib/lua_selectors/transforms.lua
index fc929175f..f6a0b0a37 100644
--- a/lualib/lua_selectors/transforms.lua
+++ b/lualib/lua_selectors/transforms.lua
@@ -113,6 +113,37 @@ local transform_function = {
['description'] = 'Joins strings into a single string using separator in the argument',
['args_schema'] = {ts.string:is_optional()}
},
+ -- Joins strings into a set of strings using N elements and a separator in the argument
+ ['join_nth'] = {
+ ['types'] = {
+ ['string_list'] = true
+ },
+ ['process'] = function(inp, _, args)
+ local step = args[1]
+ local sep = args[2] or ''
+ local inp_t = fun.totable(inp)
+ local res = {}
+
+ for i=1, #inp_t, step do
+ table.insert(res, table.concat(inp_t, sep, i, i + step))
+ end
+ return res,'string_list'
+ end,
+ ['description'] = 'Joins strings into a set of strings using N elements and a separator in the argument',
+ ['args_schema'] = {ts.number + ts.string / tonumber, ts.string:is_optional()}
+ },
+ -- Joins tables into a table of strings
+ ['join_tables'] = {
+ ['types'] = {
+ ['string_list'] = true
+ },
+ ['process'] = function(inp, _, args)
+ local sep = args[1] or ''
+ return fun.map(function(t) return table.concat(t, sep) end, inp), 'string_list'
+ end,
+ ['description'] = 'Joins tables into a table of strings',
+ ['args_schema'] = {ts.string:is_optional()}
+ },
-- Sort strings
['sort'] = {
['types'] = {
@@ -480,6 +511,23 @@ Empty string comes the first argument or 'true', non-empty string comes nil]],
['description'] = 'Removes all nils from a list of strings (when converted implicitly)',
['args_schema'] = {}
},
+ -- Call a set of methods on a userdata object
+ ['apply_methods'] = {
+ ['types'] = {
+ ['userdata'] = true,
+ },
+ ['process'] = function(inp, _, args)
+ local res = {}
+ for _,arg in ipairs(args) do
+ local meth = inp[arg]
+ local ret = meth(inp)
+ if not ret then return nil end
+ table.insert(res, tostring(ret))
+ end
+ return res,'string_list'
+ end,
+ ['description'] = 'Apply a list of method calls to the userdata object',
+ }
}
transform_function.match = transform_function.regexp