1use std::collections::btree_map;
2
3use crate::compiler::prelude::*;
4use std::sync::LazyLock;
5
6static DEFAULT_SEPARATOR: LazyLock<Value> = LazyLock::new(|| Value::Bytes(Bytes::from(".")));
7
8static PARAMETERS: LazyLock<Vec<Parameter>> = LazyLock::new(|| {
9 vec![
10 Parameter::required(
11 "value",
12 kind::OBJECT | kind::ARRAY,
13 "The array or object to flatten.",
14 ),
15 Parameter::optional(
16 "separator",
17 kind::BYTES,
18 "The separator to join nested keys",
19 )
20 .default(&DEFAULT_SEPARATOR),
21 ]
22});
23
24fn flatten(value: Value, separator: &Value) -> Resolved {
25 let separator = separator.try_bytes_utf8_lossy()?;
26
27 match value {
28 Value::Array(arr) => Ok(Value::Array(
29 ArrayFlatten::new(arr.iter()).cloned().collect(),
30 )),
31 Value::Object(map) => Ok(Value::Object(
32 MapFlatten::new(map.iter(), &separator)
33 .map(|(k, v)| (k, v.clone()))
34 .collect(),
35 )),
36 value => Err(ValueError::Expected {
37 got: value.kind(),
38 expected: Kind::array(Collection::any()) | Kind::object(Collection::any()),
39 }
40 .into()),
41 }
42}
43
44#[derive(Clone, Copy, Debug)]
45pub struct Flatten;
46
47impl Function for Flatten {
48 fn identifier(&self) -> &'static str {
49 "flatten"
50 }
51
52 fn usage(&self) -> &'static str {
53 "Flattens the `value` into a single-level representation."
54 }
55
56 fn category(&self) -> &'static str {
57 Category::Enumerate.as_ref()
58 }
59
60 fn return_kind(&self) -> u16 {
61 kind::ARRAY | kind::OBJECT
62 }
63
64 fn return_rules(&self) -> &'static [&'static str] {
65 &["The return type matches the `value` type."]
66 }
67
68 fn parameters(&self) -> &'static [Parameter] {
69 PARAMETERS.as_slice()
70 }
71
72 fn examples(&self) -> &'static [Example] {
73 &[
74 example! {
75 title: "Flatten array",
76 source: "flatten([1, [2, 3, 4], [5, [6, 7], 8], 9])",
77 result: Ok("[1, 2, 3, 4, 5, 6, 7, 8, 9]"),
78 },
79 example! {
80 title: "Flatten object",
81 source: indoc! {r#"
82 flatten({
83 "parent1": {
84 "child1": 1,
85 "child2": 2
86 },
87 "parent2": {
88 "child3": 3
89 }
90 })
91 "#},
92 result: Ok(r#"{ "parent1.child1": 1, "parent1.child2": 2, "parent2.child3": 3 }"#),
93 },
94 example! {
95 title: "Flatten object with custom separator",
96 source: r#"flatten({ "foo": { "bar": true }}, "_")"#,
97 result: Ok(r#"{ "foo_bar": true }"#),
98 },
99 ]
100 }
101
102 fn compile(
103 &self,
104 _state: &state::TypeState,
105 _ctx: &mut FunctionCompileContext,
106 arguments: ArgumentList,
107 ) -> Compiled {
108 let separator = arguments.optional("separator");
109 let value = arguments.required("value");
110 Ok(FlattenFn { value, separator }.as_expr())
111 }
112}
113
114#[derive(Debug, Clone)]
115struct FlattenFn {
116 value: Box<dyn Expression>,
117 separator: Option<Box<dyn Expression>>,
118}
119
120impl FunctionExpression for FlattenFn {
121 fn resolve(&self, ctx: &mut Context) -> Resolved {
122 let value = self.value.resolve(ctx)?;
123 let separator = self
124 .separator
125 .map_resolve_with_default(ctx, || DEFAULT_SEPARATOR.clone())?;
126
127 flatten(value, &separator)
128 }
129
130 fn type_def(&self, state: &state::TypeState) -> TypeDef {
131 let td = self.value.type_def(state);
132
133 if td.is_array() {
134 TypeDef::array(Collection::any())
135 } else {
136 TypeDef::object(Collection::any())
137 }
138 }
139}
140
141struct MapFlatten<'a> {
143 values: btree_map::Iter<'a, KeyString, Value>,
144 separator: &'a str,
145 inner: Option<Box<MapFlatten<'a>>>,
146 parent: Option<KeyString>,
147}
148
149impl<'a> MapFlatten<'a> {
150 fn new(values: btree_map::Iter<'a, KeyString, Value>, separator: &'a str) -> Self {
151 Self {
152 values,
153 separator,
154 inner: None,
155 parent: None,
156 }
157 }
158
159 fn new_from_parent(
160 parent: KeyString,
161 values: btree_map::Iter<'a, KeyString, Value>,
162 separator: &'a str,
163 ) -> Self {
164 Self {
165 values,
166 separator,
167 inner: None,
168 parent: Some(parent),
169 }
170 }
171
172 fn new_key(&self, key: &str) -> KeyString {
174 match self.parent {
175 None => key.to_string().into(),
176 Some(ref parent) => format!("{parent}{}{key}", self.separator).into(),
177 }
178 }
179}
180
181impl<'a> std::iter::Iterator for MapFlatten<'a> {
182 type Item = (KeyString, &'a Value);
183
184 fn next(&mut self) -> Option<Self::Item> {
185 if let Some(ref mut inner) = self.inner {
186 let next = inner.next();
187 match next {
188 Some(_) => return next,
189 None => self.inner = None,
190 }
191 }
192
193 let next = self.values.next();
194 match next {
195 Some((key, Value::Object(value))) => {
196 self.inner = Some(Box::new(MapFlatten::new_from_parent(
197 self.new_key(key),
198 value.iter(),
199 self.separator,
200 )));
201 self.next()
202 }
203 Some((key, value)) => Some((self.new_key(key), value)),
204 None => None,
205 }
206 }
207}
208
209struct ArrayFlatten<'a> {
212 values: std::slice::Iter<'a, Value>,
213 inner: Option<Box<ArrayFlatten<'a>>>,
214}
215
216impl<'a> ArrayFlatten<'a> {
217 fn new(values: std::slice::Iter<'a, Value>) -> Self {
218 ArrayFlatten {
219 values,
220 inner: None,
221 }
222 }
223}
224
225impl<'a> std::iter::Iterator for ArrayFlatten<'a> {
226 type Item = &'a Value;
227
228 fn next(&mut self) -> Option<Self::Item> {
229 if let Some(ref mut inner) = self.inner {
231 let next = inner.next();
232 match next {
233 Some(_) => return next,
234 None => {
235 self.inner = None;
237 }
238 }
239 }
240
241 let next = self.values.next();
243 match next {
244 Some(Value::Array(next)) => {
245 self.inner = Some(Box::new(ArrayFlatten::new(next.iter())));
247 self.next()
248 }
249 _ => next,
250 }
251 }
252}
253
254#[cfg(test)]
255mod test {
256 use super::*;
257 use crate::value;
258
259 test_function![
260 flatten => Flatten;
261
262 array {
263 args: func_args![value: value!([42])],
264 want: Ok(value!([42])),
265 tdef: TypeDef::array(Collection::any()),
266 }
267
268 nested_array {
269 args: func_args![value: value!([42, [43, 44]])],
270 want: Ok(value!([42, 43, 44])),
271 tdef: TypeDef::array(Collection::any()),
272 }
273
274 nested_empty_array {
275 args: func_args![value: value!([42, [], 43])],
276 want: Ok(value!([42, 43])),
277 tdef: TypeDef::array(Collection::any()),
278 }
279
280 double_nested_array {
281 args: func_args![value: value!([42, [43, 44, [45, 46]]])],
282 want: Ok(value!([42, 43, 44, 45, 46])),
283 tdef: TypeDef::array(Collection::any()),
284 }
285
286 two_arrays {
287 args: func_args![value: value!([[42, 43], [44, 45]])],
288 want: Ok(value!([42, 43, 44, 45])),
289 tdef: TypeDef::array(Collection::any()),
290 }
291
292 map {
293 args: func_args![value: value!({parent: "child"})],
294 want: Ok(value!({parent: "child"})),
295 tdef: TypeDef::object(Collection::any()),
296 }
297
298 nested_map {
299 args: func_args![value: value!({parent: {child1: 1, child2: 2}, key: "val"})],
300 want: Ok(value!({"parent.child1": 1, "parent.child2": 2, key: "val"})),
301 tdef: TypeDef::object(Collection::any()),
302 }
303
304 nested_map_with_separator {
305 args: func_args![value: value!({parent: {child1: 1, child2: 2}, key: "val"}), separator: "_"],
306 want: Ok(value!({"parent_child1": 1, "parent_child2": 2, key: "val"})),
307 tdef: TypeDef::object(Collection::any()),
308 }
309
310 double_nested_map {
311 args: func_args![value: value!({
312 parent: {
313 child1: 1,
314 child2: { grandchild1: 1, grandchild2: 2 },
315 },
316 key: "val",
317 })],
318 want: Ok(value!({
319 "parent.child1": 1,
320 "parent.child2.grandchild1": 1,
321 "parent.child2.grandchild2": 2,
322 key: "val",
323 })),
324 tdef: TypeDef::object(Collection::any()),
325 }
326
327 map_and_array {
328 args: func_args![value: value!({
329 parent: {
330 child1: [1, [2, 3]],
331 child2: {grandchild1: 1, grandchild2: [1, [2, 3], 4]},
332 },
333 key: "val",
334 })],
335 want: Ok(value!({
336 "parent.child1": [1, [2, 3]],
337 "parent.child2.grandchild1": 1,
338 "parent.child2.grandchild2": [1, [2, 3], 4],
339 key: "val",
340 })),
341 tdef: TypeDef::object(Collection::any()),
342 }
343
344 map_and_array_with_separator {
345 args: func_args![value: value!({
346 parent: {
347 child1: [1, [2, 3]],
348 child2: {grandchild1: 1, grandchild2: [1, [2, 3], 4]},
349 },
350 key: "val",
351 }), separator: "_"],
352 want: Ok(value!({
353 "parent_child1": [1, [2, 3]],
354 "parent_child2_grandchild1": 1,
355 "parent_child2_grandchild2": [1, [2, 3], 4],
356 key: "val",
357 })),
358 tdef: TypeDef::object(Collection::any()),
359 }
360
361 root_array {
363 args: func_args![value: value!([
364 { parent1: { child1: 1, child2: 2 } },
365 [
366 { parent2: { child3: 3, child4: 4 } },
367 { parent3: { child5: 5 } },
368 ],
369 ])],
370 want: Ok(value!([
371 { parent1: { child1: 1, child2: 2 } },
372 { parent2: { child3: 3, child4: 4 } },
373 { parent3: { child5: 5 } },
374 ])),
375 tdef: TypeDef::array(Collection::any()),
376 }
377
378 triple_nested_map {
379 args: func_args![value: value!({
380 parent1: {
381 child1: { grandchild1: 1 },
382 child2: { grandchild2: 2, grandchild3: 3 },
383 },
384 parent2: 4,
385 })],
386 want: Ok(value!({
387 "parent1.child1.grandchild1": 1,
388 "parent1.child2.grandchild2": 2,
389 "parent1.child2.grandchild3": 3,
390 parent2: 4,
391 })),
392 tdef: TypeDef::object(Collection::any()),
393 }
394 ];
395}