vrl/stdlib/
filter.rs

1use crate::compiler::prelude::*;
2use std::collections::BTreeMap;
3
4fn filter<T>(value: Value, ctx: &mut Context, runner: &closure::Runner<T>) -> Resolved
5where
6    T: Fn(&mut Context) -> Resolved,
7{
8    match value {
9        Value::Object(object) => object
10            .into_iter()
11            .filter_map(
12                |(key, value)| match runner.run_key_value(ctx, &key, &value) {
13                    Ok(v) => v
14                        .as_boolean()
15                        .expect("compiler guarantees boolean return type")
16                        .then_some(Ok((key, value))),
17                    Err(err) => Some(Err(err)),
18                },
19            )
20            .collect::<ExpressionResult<BTreeMap<_, _>>>()
21            .map(Into::into),
22
23        Value::Array(array) => array
24            .into_iter()
25            .enumerate()
26            .filter_map(
27                |(index, value)| match runner.run_index_value(ctx, index, &value) {
28                    Ok(v) => v
29                        .as_boolean()
30                        .expect("compiler guarantees boolean return type")
31                        .then_some(Ok(value)),
32                    Err(err) => Some(Err(err)),
33                },
34            )
35            .collect::<ExpressionResult<Vec<_>>>()
36            .map(Into::into),
37
38        _ => Err("function requires collection types as input".into()),
39    }
40}
41
42#[derive(Clone, Copy, Debug)]
43pub struct Filter;
44
45impl Function for Filter {
46    fn identifier(&self) -> &'static str {
47        "filter"
48    }
49
50    fn usage(&self) -> &'static str {
51        indoc! {"
52            Filter elements from a collection.
53
54            This function currently *does not* support recursive iteration.
55
56            The function uses the function closure syntax to allow reading
57            the key-value or index-value combination for each item in the
58            collection.
59
60            The same scoping rules apply to closure blocks as they do for
61            regular blocks. This means that any variable defined in parent scopes
62            is accessible, and mutations to those variables are preserved,
63            but any new variables instantiated in the closure block are
64            unavailable outside of the block.
65
66            See the examples below to learn about the closure syntax.
67        "}
68    }
69
70    fn parameters(&self) -> &'static [Parameter] {
71        &[Parameter {
72            keyword: "value",
73            kind: kind::OBJECT | kind::ARRAY,
74            required: true,
75        }]
76    }
77
78    fn examples(&self) -> &'static [Example] {
79        &[
80            example! {
81                title: "Filter elements",
82                source: indoc! {r#"
83                    . = { "tags": ["foo", "bar", "foo", "baz"] }
84                    filter(array(.tags)) -> |_index, value| {
85                        value != "foo"
86                    }
87                "#},
88                result: Ok(r#"["bar", "baz"]"#),
89            },
90            example! {
91                title: "Filter object",
92                source: r#"filter({ "a": 1, "b": 2 }) -> |key, _value| { key == "a" }"#,
93                result: Ok(r#"{ "a": 1 }"#),
94            },
95            example! {
96                title: "Filter array",
97                source: "filter([1, 2]) -> |_index, value| { value < 2 }",
98                result: Ok("[1]"),
99            },
100        ]
101    }
102
103    fn compile(
104        &self,
105        _state: &state::TypeState,
106        _ctx: &mut FunctionCompileContext,
107        arguments: ArgumentList,
108    ) -> Compiled {
109        let value = arguments.required("value");
110        let closure = arguments.required_closure()?;
111
112        Ok(FilterFn { value, closure }.as_expr())
113    }
114
115    fn closure(&self) -> Option<closure::Definition> {
116        use closure::{Definition, Input, Output, Variable, VariableKind};
117
118        Some(Definition {
119            inputs: vec![Input {
120                parameter_keyword: "value",
121                kind: Kind::object(Collection::any()).or_array(Collection::any()),
122                variables: vec![
123                    Variable {
124                        kind: VariableKind::TargetInnerKey,
125                    },
126                    Variable {
127                        kind: VariableKind::TargetInnerValue,
128                    },
129                ],
130                output: Output::Kind(Kind::boolean()),
131                example: example! {
132                    title: "filter array",
133                    source: "filter([1, 2]) -> |index, _value| { index == 0 }",
134                    result: Ok("[1]"),
135                },
136            }],
137            is_iterator: true,
138        })
139    }
140}
141
142#[derive(Debug, Clone)]
143struct FilterFn {
144    value: Box<dyn Expression>,
145    closure: Closure,
146}
147
148impl FunctionExpression for FilterFn {
149    fn resolve(&self, ctx: &mut Context) -> ExpressionResult<Value> {
150        let value = self.value.resolve(ctx)?;
151        let Closure {
152            variables,
153            block,
154            block_type_def: _,
155        } = &self.closure;
156        let runner = closure::Runner::new(variables, |ctx| block.resolve(ctx));
157
158        filter(value, ctx, &runner)
159    }
160
161    fn type_def(&self, ctx: &state::TypeState) -> TypeDef {
162        let mut type_def = self.value.type_def(ctx);
163
164        // Erase any type information from the array or object, as we can't know
165        // which elements are removed at runtime.
166        if type_def.contains_array() {
167            type_def.kind_mut().add_array(Collection::any());
168        }
169
170        if type_def.contains_object() {
171            type_def.kind_mut().add_object(Collection::any());
172        }
173
174        type_def
175    }
176}