vrl/stdlib/
for_each.rs

1use crate::compiler::prelude::*;
2
3fn for_each<T>(value: Value, ctx: &mut Context, runner: &closure::Runner<T>) -> Resolved
4where
5    T: Fn(&mut Context) -> Resolved,
6{
7    for item in value.into_iter(false) {
8        match item {
9            IterItem::KeyValue(key, value) => {
10                runner.run_key_value(ctx, key, value)?;
11            }
12            IterItem::IndexValue(index, value) => {
13                runner.run_index_value(ctx, index, value)?;
14            }
15            IterItem::Value(_) => {}
16        }
17    }
18
19    Ok(Value::Null)
20}
21
22#[derive(Clone, Copy, Debug)]
23pub struct ForEach;
24
25impl Function for ForEach {
26    fn identifier(&self) -> &'static str {
27        "for_each"
28    }
29
30    fn usage(&self) -> &'static str {
31        indoc! {"
32            Iterate over a collection.
33
34            This function currently *does not* support recursive iteration.
35
36            The function uses the \"function closure syntax\" to allow reading
37            the key/value or index/value combination for each item in the
38            collection.
39
40            The same scoping rules apply to closure blocks as they do for
41            regular blocks. This means that any variable defined in parent scopes
42            is accessible, and mutations to those variables are preserved,
43            but any new variables instantiated in the closure block are
44            unavailable outside of the block.
45
46            See the examples below to learn about the closure syntax.
47        "}
48    }
49
50    fn category(&self) -> &'static str {
51        Category::Enumerate.as_ref()
52    }
53
54    fn return_kind(&self) -> u16 {
55        kind::NULL
56    }
57    fn parameters(&self) -> &'static [Parameter] {
58        const PARAMETERS: &[Parameter] = &[Parameter::required(
59            "value",
60            kind::OBJECT | kind::ARRAY,
61            "The array or object to iterate.",
62        )];
63        PARAMETERS
64    }
65
66    fn examples(&self) -> &'static [Example] {
67        &[
68            example! {
69                title: "Tally elements",
70                source: indoc! {r#"
71                    .tags = ["foo", "bar", "foo", "baz"]
72                    tally = {}
73                    for_each(array(.tags)) -> |_index, value| {
74                        count = int(get!(tally, [value])) ?? 0
75                        tally = set!(tally, [value], count + 1)
76                    }
77                    tally
78                "#},
79                result: Ok(r#"{"bar": 1, "baz": 1, "foo": 2}"#),
80            },
81            example! {
82                title: "Iterate over an object",
83                source: indoc! {r#"
84                    count = 0
85                    for_each({ "a": 1, "b": 2 }) -> |_key, value| {
86                        count = count + value
87                    }
88                    count
89                "#},
90                result: Ok("3"),
91            },
92            example! {
93                title: "Iterate over an array",
94                source: indoc! {"
95                    count = 0
96                    for_each([1, 2, 3]) -> |index, value| {
97                        count = count + index + value
98                    }
99                    count
100                "},
101                result: Ok("9"),
102            },
103        ]
104    }
105
106    fn compile(
107        &self,
108        _state: &state::TypeState,
109        _ctx: &mut FunctionCompileContext,
110        arguments: ArgumentList,
111    ) -> Compiled {
112        let value = arguments.required("value");
113        let closure = arguments.required_closure()?;
114
115        Ok(ForEachFn { value, closure }.as_expr())
116    }
117
118    fn closure(&self) -> Option<closure::Definition> {
119        use closure::{Definition, Input, Output, Variable, VariableKind};
120
121        Some(Definition {
122            inputs: vec![Input {
123                parameter_keyword: "value",
124                kind: Kind::object(Collection::any()).or_array(Collection::any()),
125                variables: vec![
126                    Variable {
127                        kind: VariableKind::TargetInnerKey,
128                    },
129                    Variable {
130                        kind: VariableKind::TargetInnerValue,
131                    },
132                ],
133                output: Output::Kind(Kind::any()),
134                example: example! {
135                    title: "iterate array",
136                    source: "for_each([1, 2]) -> |index, value| { .foo = to_int!(.foo) + index + value }",
137                    result: Ok("null"),
138                },
139            }],
140            is_iterator: true,
141        })
142    }
143}
144
145#[derive(Debug, Clone)]
146struct ForEachFn {
147    value: Box<dyn Expression>,
148    closure: Closure,
149}
150
151impl FunctionExpression for ForEachFn {
152    fn resolve(&self, ctx: &mut Context) -> ExpressionResult<Value> {
153        let value = self.value.resolve(ctx)?;
154        let Closure {
155            variables,
156            block,
157            block_type_def: _,
158        } = &self.closure;
159        let runner = closure::Runner::new(variables, |ctx| block.resolve(ctx));
160
161        for_each(value, ctx, &runner)
162    }
163
164    fn type_def(&self, _ctx: &state::TypeState) -> TypeDef {
165        TypeDef::null()
166    }
167}