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}