1use crate::compiler::prelude::*;
2use std::sync::LazyLock;
3
4static DEFAULT_RECURSIVE: LazyLock<Value> = LazyLock::new(|| Value::Boolean(false));
5
6static PARAMETERS: LazyLock<Vec<Parameter>> = LazyLock::new(|| {
7 vec![
8 Parameter::required(
9 "value",
10 kind::OBJECT | kind::ARRAY,
11 "The object or array to iterate.",
12 ),
13 Parameter::optional(
14 "recursive",
15 kind::BOOLEAN,
16 "Whether to recursively iterate the collection.",
17 )
18 .default(&DEFAULT_RECURSIVE),
19 ]
20});
21
22fn map_values<T>(
23 value: Value,
24 recursive: bool,
25 ctx: &mut Context,
26 runner: &closure::Runner<T>,
27) -> Resolved
28where
29 T: Fn(&mut Context) -> Resolved,
30{
31 let mut iter = value.into_iter(recursive);
32
33 for item in iter.by_ref() {
34 let value = match item {
35 IterItem::KeyValue(_, value)
36 | IterItem::IndexValue(_, value)
37 | IterItem::Value(value) => value,
38 };
39
40 runner.map_value(ctx, value)?;
41 }
42
43 Ok(iter.into())
44}
45
46#[derive(Clone, Copy, Debug)]
47pub struct MapValues;
48
49impl Function for MapValues {
50 fn identifier(&self) -> &'static str {
51 "map_values"
52 }
53
54 fn usage(&self) -> &'static str {
55 indoc! {"
56 Map the values within a collection.
57
58 If `recursive` is enabled, the function iterates into nested
59 collections, using the following rules:
60
61 1. Iteration starts at the root.
62 2. For every nested collection type:
63 - First return the collection type itself.
64 - Then recurse into the collection, and loop back to item (1)
65 in the list
66 - Any mutation done on a collection *before* recursing into it,
67 are preserved.
68
69 The function uses the function closure syntax to allow mutating
70 the value for each item in the collection.
71
72 The same scoping rules apply to closure blocks as they do for
73 regular blocks, meaning, any variable defined in parent scopes
74 are accessible, and mutations to those variables are preserved,
75 but any new variables instantiated in the closure block are
76 unavailable outside of the block.
77
78 Check out the examples below to learn about the closure syntax.
79 "}
80 }
81
82 fn category(&self) -> &'static str {
83 Category::Enumerate.as_ref()
84 }
85
86 fn return_kind(&self) -> u16 {
87 kind::ARRAY | kind::OBJECT
88 }
89
90 fn parameters(&self) -> &'static [Parameter] {
91 PARAMETERS.as_slice()
92 }
93
94 fn examples(&self) -> &'static [Example] {
95 &[
96 example! {
97 title: "Upcase values",
98 source: indoc! {r#"
99 . = {
100 "foo": "foo",
101 "bar": "bar"
102 }
103 map_values(.) -> |value| { upcase(value) }
104 "#},
105 result: Ok(r#"{ "foo": "FOO", "bar": "BAR" }"#),
106 },
107 example! {
108 title: "Recursively map object values",
109 source: indoc! {r#"
110 val = {
111 "a": 1,
112 "b": [{ "c": 2 }, { "d": 3 }],
113 "e": { "f": 4 }
114 }
115 map_values(val, recursive: true) -> |value| {
116 if is_integer(value) { int!(value) + 1 } else { value }
117 }
118 "#},
119 result: Ok(r#"{ "a": 2, "b": [{ "c": 3 }, { "d": 4 }], "e": { "f": 5 } }"#),
120 },
121 ]
122 }
123
124 fn compile(
125 &self,
126 _state: &state::TypeState,
127 _ctx: &mut FunctionCompileContext,
128 arguments: ArgumentList,
129 ) -> Compiled {
130 let value = arguments.required("value");
131 let recursive = arguments.optional("recursive");
132 let closure = arguments.required_closure()?;
133
134 Ok(MapValuesFn {
135 value,
136 recursive,
137 closure,
138 }
139 .as_expr())
140 }
141
142 fn closure(&self) -> Option<closure::Definition> {
143 use closure::{Definition, Input, Output, Variable, VariableKind};
144
145 Some(Definition {
146 inputs: vec![Input {
147 parameter_keyword: "value",
148 kind: Kind::object(Collection::any()).or_array(Collection::any()),
149 variables: vec![Variable {
150 kind: VariableKind::TargetInnerValue,
151 }],
152 output: Output::Kind(Kind::any()),
153 example: example! {
154 title: "map object values",
155 source: r#"map_values({ "one" : "one", "two": "two" }) -> |value| { upcase(value) }"#,
156 result: Ok(r#"{ "one": "ONE", "two": "TWO" }"#),
157 },
158 }],
159 is_iterator: true,
160 })
161 }
162}
163
164#[derive(Debug, Clone)]
165struct MapValuesFn {
166 value: Box<dyn Expression>,
167 recursive: Option<Box<dyn Expression>>,
168 closure: Closure,
169}
170
171impl FunctionExpression for MapValuesFn {
172 fn resolve(&self, ctx: &mut Context) -> ExpressionResult<Value> {
173 let recursive = self
174 .recursive
175 .map_resolve_with_default(ctx, || DEFAULT_RECURSIVE.clone())?
176 .try_boolean()?;
177
178 let value = self.value.resolve(ctx)?;
179 let Closure {
180 variables,
181 block,
182 block_type_def: _,
183 } = &self.closure;
184 let runner = closure::Runner::new(variables, |ctx| block.resolve(ctx));
185
186 map_values(value, recursive, ctx, &runner)
187 }
188
189 fn type_def(&self, ctx: &state::TypeState) -> TypeDef {
190 let mut value = self.value.type_def(ctx);
191 let closure = self.closure.block_type_def.kind().clone();
192
193 recursive_type_def(&mut value, closure, true);
194 value
195 }
196}
197
198fn recursive_type_def(from: &mut Kind, to: Kind, root: bool) {
199 if let Some(object) = from.as_object_mut() {
200 for v in object.known_mut().values_mut() {
201 recursive_type_def(v, to.clone(), false);
202 }
203 }
204
205 if let Some(array) = from.as_array_mut() {
206 for v in array.known_mut().values_mut() {
207 recursive_type_def(v, to.clone(), false);
208 }
209 }
210
211 if !root {
212 *from = to;
213 }
214}