tera/builtins/filters/
array.rs

1/// Filters operating on array
2use std::collections::HashMap;
3
4use crate::context::{dotted_pointer, ValueRender};
5use crate::errors::{Error, Result};
6use crate::filter_utils::{get_sort_strategy_for_type, get_unique_strategy_for_type};
7use crate::utils::render_to_string;
8use serde_json::value::{to_value, Map, Value};
9
10/// Returns the nth value of an array
11/// If the array is empty, returns empty string
12pub fn nth(value: &Value, args: &HashMap<String, Value>) -> Result<Value> {
13    let arr = try_get_value!("nth", "value", Vec<Value>, value);
14
15    if arr.is_empty() {
16        return Ok(to_value("").unwrap());
17    }
18
19    let index = match args.get("n") {
20        Some(val) => try_get_value!("nth", "n", usize, val),
21        None => return Err(Error::msg("The `nth` filter has to have an `n` argument")),
22    };
23
24    Ok(arr.get(index).unwrap_or(&to_value("").unwrap()).to_owned())
25}
26
27/// Returns the first value of an array
28/// If the array is empty, returns empty string
29pub fn first(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
30    let mut arr = try_get_value!("first", "value", Vec<Value>, value);
31
32    if arr.is_empty() {
33        Ok(to_value("").unwrap())
34    } else {
35        Ok(arr.swap_remove(0))
36    }
37}
38
39/// Returns the last value of an array
40/// If the array is empty, returns empty string
41pub fn last(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
42    let mut arr = try_get_value!("last", "value", Vec<Value>, value);
43
44    Ok(arr.pop().unwrap_or_else(|| to_value("").unwrap()))
45}
46
47/// Joins all values in the array by the `sep` argument given
48/// If no separator is given, it will use `""` (empty string) as separator
49/// If the array is empty, returns empty string
50pub fn join(value: &Value, args: &HashMap<String, Value>) -> Result<Value> {
51    let arr = try_get_value!("join", "value", Vec<Value>, value);
52    let sep = match args.get("sep") {
53        Some(val) => {
54            let s = try_get_value!("truncate", "sep", String, val);
55            // When reading from a file, it will escape `\n` to `\\n` for example so we need
56            // to replace double escape. In practice it might cause issues if someone wants to join
57            // with `\\n` for real but that seems pretty unlikely
58            s.replace("\\n", "\n").replace("\\t", "\t")
59        }
60        None => String::new(),
61    };
62
63    // Convert all the values to strings before we join them together.
64    let rendered = arr
65        .iter()
66        .map(|v| render_to_string(|| "joining array".to_string(), |w| v.render(w)))
67        .collect::<Result<Vec<_>>>()?;
68    to_value(rendered.join(&sep)).map_err(Error::json)
69}
70
71/// Sorts the array in ascending order.
72/// Use the 'attribute' argument to define a field to sort by.
73pub fn sort(value: &Value, args: &HashMap<String, Value>) -> Result<Value> {
74    let arr = try_get_value!("sort", "value", Vec<Value>, value);
75    if arr.is_empty() {
76        return Ok(arr.into());
77    }
78
79    let attribute = match args.get("attribute") {
80        Some(val) => try_get_value!("sort", "attribute", String, val),
81        None => String::new(),
82    };
83
84    let first = dotted_pointer(&arr[0], &attribute).ok_or_else(|| {
85        Error::msg(format!("attribute '{}' does not reference a field", attribute))
86    })?;
87
88    let mut strategy = get_sort_strategy_for_type(first)?;
89    for v in &arr {
90        let key = dotted_pointer(v, &attribute).ok_or_else(|| {
91            Error::msg(format!("attribute '{}' does not reference a field", attribute))
92        })?;
93        strategy.try_add_pair(v, key)?;
94    }
95    let sorted = strategy.sort();
96
97    Ok(sorted.into())
98}
99
100/// Remove duplicates from an array.
101/// Use the 'attribute' argument to define a field to filter on.
102/// For strings, use the 'case_sensitive' argument (defaults to false) to control the comparison.
103pub fn unique(value: &Value, args: &HashMap<String, Value>) -> Result<Value> {
104    let arr = try_get_value!("unique", "value", Vec<Value>, value);
105    if arr.is_empty() {
106        return Ok(arr.into());
107    }
108
109    let case_sensitive = match args.get("case_sensitive") {
110        Some(val) => try_get_value!("unique", "case_sensitive", bool, val),
111        None => false,
112    };
113
114    let attribute = match args.get("attribute") {
115        Some(val) => try_get_value!("unique", "attribute", String, val),
116        None => String::new(),
117    };
118
119    let first = dotted_pointer(&arr[0], &attribute).ok_or_else(|| {
120        Error::msg(format!("attribute '{}' does not reference a field", attribute))
121    })?;
122
123    let disc = std::mem::discriminant(first);
124    let mut strategy = get_unique_strategy_for_type(first, case_sensitive)?;
125
126    let arr = arr
127        .into_iter()
128        .filter_map(|v| match dotted_pointer(&v, &attribute) {
129            Some(key) => {
130                if disc == std::mem::discriminant(key) {
131                    match strategy.insert(key) {
132                        Ok(false) => None,
133                        Ok(true) => Some(Ok(v)),
134                        Err(e) => Some(Err(e)),
135                    }
136                } else {
137                    Some(Err(Error::msg("unique filter can't compare multiple types")))
138                }
139            }
140            None => None,
141        })
142        .collect::<Result<Vec<_>>>();
143
144    Ok(to_value(arr?).unwrap())
145}
146
147/// Group the array values by the `attribute` given
148/// Returns a hashmap of key => values, items without the `attribute` or where `attribute` is `null` are discarded.
149/// The returned keys are stringified
150pub fn group_by(value: &Value, args: &HashMap<String, Value>) -> Result<Value> {
151    let arr = try_get_value!("group_by", "value", Vec<Value>, value);
152    if arr.is_empty() {
153        return Ok(Map::new().into());
154    }
155
156    let key = match args.get("attribute") {
157        Some(val) => try_get_value!("group_by", "attribute", String, val),
158        None => {
159            return Err(Error::msg("The `group_by` filter has to have an `attribute` argument"))
160        }
161    };
162
163    let mut grouped = Map::new();
164
165    for val in arr {
166        if let Some(key_val) = dotted_pointer(&val, &key).cloned() {
167            if key_val.is_null() {
168                continue;
169            }
170
171            let str_key = match key_val.as_str() {
172                Some(key) => key.to_owned(),
173                None => format!("{}", key_val),
174            };
175
176            if let Some(vals) = grouped.get_mut(&str_key) {
177                vals.as_array_mut().unwrap().push(val);
178                continue;
179            }
180
181            grouped.insert(str_key, Value::Array(vec![val]));
182        }
183    }
184
185    Ok(to_value(grouped).unwrap())
186}
187
188/// Filter the array values, returning only the values where the `attribute` is equal to the `value`
189/// Values without the `attribute` or with a null `attribute` are discarded
190/// If the `value` is not passed, discard all elements where the attribute is null.
191pub fn filter(value: &Value, args: &HashMap<String, Value>) -> Result<Value> {
192    let mut arr = try_get_value!("filter", "value", Vec<Value>, value);
193    if arr.is_empty() {
194        return Ok(arr.into());
195    }
196
197    let key = match args.get("attribute") {
198        Some(val) => try_get_value!("filter", "attribute", String, val),
199        None => return Err(Error::msg("The `filter` filter has to have an `attribute` argument")),
200    };
201    let value = args.get("value").unwrap_or(&Value::Null);
202
203    arr = arr
204        .into_iter()
205        .filter(|v| {
206            let val = dotted_pointer(v, &key).unwrap_or(&Value::Null);
207            if value.is_null() {
208                !val.is_null()
209            } else {
210                val == value
211            }
212        })
213        .collect::<Vec<_>>();
214
215    Ok(to_value(arr).unwrap())
216}
217
218/// Map retrieves an attribute from a list of objects.
219/// The 'attribute' argument specifies what to retrieve.
220pub fn map(value: &Value, args: &HashMap<String, Value>) -> Result<Value> {
221    let arr = try_get_value!("map", "value", Vec<Value>, value);
222    if arr.is_empty() {
223        return Ok(arr.into());
224    }
225
226    let attribute = match args.get("attribute") {
227        Some(val) => try_get_value!("map", "attribute", String, val),
228        None => return Err(Error::msg("The `map` filter has to have an `attribute` argument")),
229    };
230
231    let arr = arr
232        .into_iter()
233        .filter_map(|v| match dotted_pointer(&v, &attribute) {
234            Some(val) if !val.is_null() => Some(val.clone()),
235            _ => None,
236        })
237        .collect::<Vec<_>>();
238
239    Ok(to_value(arr).unwrap())
240}
241
242#[inline]
243fn get_index(i: f64, array: &[Value]) -> usize {
244    if i >= 0.0 {
245        i as usize
246    } else {
247        (array.len() as f64 + i) as usize
248    }
249}
250
251/// Slice the array
252/// Use the `start` argument to define where to start (inclusive, default to `0`)
253/// and `end` argument to define where to stop (exclusive, default to the length of the array)
254/// `start` and `end` are 0-indexed
255pub fn slice(value: &Value, args: &HashMap<String, Value>) -> Result<Value> {
256    let arr = try_get_value!("slice", "value", Vec<Value>, value);
257    if arr.is_empty() {
258        return Ok(arr.into());
259    }
260
261    let start = match args.get("start") {
262        Some(val) => get_index(try_get_value!("slice", "start", f64, val), &arr),
263        None => 0,
264    };
265
266    let mut end = match args.get("end") {
267        Some(val) => get_index(try_get_value!("slice", "end", f64, val), &arr),
268        None => arr.len(),
269    };
270
271    if end > arr.len() {
272        end = arr.len();
273    }
274
275    // Not an error, but returns an empty Vec
276    if start >= end {
277        return Ok(Vec::<Value>::new().into());
278    }
279
280    Ok(arr[start..end].into())
281}
282
283/// Concat the array with another one if the `with` parameter is an array or
284/// just append it otherwise
285pub fn concat(value: &Value, args: &HashMap<String, Value>) -> Result<Value> {
286    let mut arr = try_get_value!("concat", "value", Vec<Value>, value);
287
288    let value = match args.get("with") {
289        Some(val) => val,
290        None => return Err(Error::msg("The `concat` filter has to have a `with` argument")),
291    };
292
293    if value.is_array() {
294        match value {
295            Value::Array(vals) => {
296                for val in vals {
297                    arr.push(val.clone());
298                }
299            }
300            _ => unreachable!("Got something other than an array??"),
301        }
302    } else {
303        arr.push(value.clone());
304    }
305
306    Ok(to_value(arr).unwrap())
307}
308
309#[cfg(test)]
310mod tests {
311    use super::*;
312    use serde_derive::{Deserialize, Serialize};
313    use serde_json::json;
314    use serde_json::value::{to_value, Value};
315    use std::collections::HashMap;
316
317    #[test]
318    fn test_nth() {
319        let mut args = HashMap::new();
320        args.insert("n".to_string(), to_value(1).unwrap());
321        let result = nth(&to_value(vec![1, 2, 3, 4]).unwrap(), &args);
322        assert!(result.is_ok());
323        assert_eq!(result.unwrap(), to_value(2).unwrap());
324    }
325
326    #[test]
327    fn test_nth_empty() {
328        let v: Vec<Value> = Vec::new();
329        let mut args = HashMap::new();
330        args.insert("n".to_string(), to_value(1).unwrap());
331        let result = nth(&to_value(v).unwrap(), &args);
332        assert!(result.is_ok());
333        assert_eq!(result.unwrap(), to_value("").unwrap());
334    }
335
336    #[test]
337    fn test_first() {
338        let result = first(&to_value(vec![1, 2, 3, 4]).unwrap(), &HashMap::new());
339        assert!(result.is_ok());
340        assert_eq!(result.unwrap(), to_value(1).unwrap());
341    }
342
343    #[test]
344    fn test_first_empty() {
345        let v: Vec<Value> = Vec::new();
346
347        let result = first(&to_value(v).unwrap(), &HashMap::new());
348        assert!(result.is_ok());
349        assert_eq!(result.ok().unwrap(), to_value("").unwrap());
350    }
351
352    #[test]
353    fn test_last() {
354        let result = last(&to_value(vec!["Hello", "World"]).unwrap(), &HashMap::new());
355        assert!(result.is_ok());
356        assert_eq!(result.unwrap(), to_value("World").unwrap());
357    }
358
359    #[test]
360    fn test_last_empty() {
361        let v: Vec<Value> = Vec::new();
362
363        let result = last(&to_value(v).unwrap(), &HashMap::new());
364        assert!(result.is_ok());
365        assert_eq!(result.ok().unwrap(), to_value("").unwrap());
366    }
367
368    #[test]
369    fn test_join_sep() {
370        let mut args = HashMap::new();
371        args.insert("sep".to_owned(), to_value("==").unwrap());
372
373        let result = join(&to_value(vec!["Cats", "Dogs"]).unwrap(), &args);
374        assert!(result.is_ok());
375        assert_eq!(result.unwrap(), to_value("Cats==Dogs").unwrap());
376    }
377
378    #[test]
379    fn test_join_sep_omitted() {
380        let result = join(&to_value(vec![1.2, 3.4]).unwrap(), &HashMap::new());
381        assert!(result.is_ok());
382        assert_eq!(result.unwrap(), to_value("1.23.4").unwrap());
383    }
384
385    #[test]
386    fn test_join_empty() {
387        let v: Vec<Value> = Vec::new();
388        let mut args = HashMap::new();
389        args.insert("sep".to_owned(), to_value("==").unwrap());
390
391        let result = join(&to_value(v).unwrap(), &args);
392        assert!(result.is_ok());
393        assert_eq!(result.unwrap(), to_value("").unwrap());
394    }
395
396    #[test]
397    fn test_join_newlines_and_tabs() {
398        let mut args = HashMap::new();
399        args.insert("sep".to_owned(), to_value(",\\n\\t").unwrap());
400        let result = join(&to_value(vec!["Cats", "Dogs"]).unwrap(), &args);
401        assert_eq!(result.unwrap(), to_value("Cats,\n\tDogs").unwrap());
402    }
403
404    #[test]
405    fn test_sort() {
406        let v = to_value(vec![3, -1, 2, 5, 4]).unwrap();
407        let args = HashMap::new();
408        let result = sort(&v, &args);
409        assert!(result.is_ok());
410        assert_eq!(result.unwrap(), to_value(vec![-1, 2, 3, 4, 5]).unwrap());
411    }
412
413    #[test]
414    fn test_sort_empty() {
415        let v = to_value(Vec::<f64>::new()).unwrap();
416        let args = HashMap::new();
417        let result = sort(&v, &args);
418        assert!(result.is_ok());
419        assert_eq!(result.unwrap(), to_value(Vec::<f64>::new()).unwrap());
420    }
421
422    #[derive(Deserialize, Eq, Hash, PartialEq, Serialize)]
423    struct Foo {
424        a: i32,
425        b: i32,
426    }
427
428    #[test]
429    fn test_sort_attribute() {
430        let v = to_value(vec![
431            Foo { a: 3, b: 5 },
432            Foo { a: 2, b: 8 },
433            Foo { a: 4, b: 7 },
434            Foo { a: 1, b: 6 },
435        ])
436        .unwrap();
437        let mut args = HashMap::new();
438        args.insert("attribute".to_string(), to_value("a").unwrap());
439
440        let result = sort(&v, &args);
441        assert!(result.is_ok());
442        assert_eq!(
443            result.unwrap(),
444            to_value(vec![
445                Foo { a: 1, b: 6 },
446                Foo { a: 2, b: 8 },
447                Foo { a: 3, b: 5 },
448                Foo { a: 4, b: 7 },
449            ])
450            .unwrap()
451        );
452    }
453
454    #[test]
455    fn test_sort_invalid_attribute() {
456        let v = to_value(vec![Foo { a: 3, b: 5 }]).unwrap();
457        let mut args = HashMap::new();
458        args.insert("attribute".to_string(), to_value("invalid_field").unwrap());
459
460        let result = sort(&v, &args);
461        assert!(result.is_err());
462        assert_eq!(
463            result.unwrap_err().to_string(),
464            "attribute 'invalid_field' does not reference a field"
465        );
466    }
467
468    #[test]
469    fn test_sort_multiple_types() {
470        let v = to_value(vec![Value::Number(12.into()), Value::Array(vec![])]).unwrap();
471        let args = HashMap::new();
472
473        let result = sort(&v, &args);
474        assert!(result.is_err());
475        assert_eq!(result.unwrap_err().to_string(), "expected number got []");
476    }
477
478    #[test]
479    fn test_sort_non_finite_numbers() {
480        let v = to_value(vec![
481            ::std::f64::NEG_INFINITY, // NaN and friends get deserialized as Null by serde.
482            ::std::f64::NAN,
483        ])
484        .unwrap();
485        let args = HashMap::new();
486
487        let result = sort(&v, &args);
488        assert!(result.is_err());
489        assert_eq!(result.unwrap_err().to_string(), "Null is not a sortable value");
490    }
491
492    #[derive(Deserialize, Eq, Hash, PartialEq, Serialize)]
493    struct TupleStruct(i32, i32);
494
495    #[test]
496    fn test_sort_tuple() {
497        let v = to_value(vec![
498            TupleStruct(0, 1),
499            TupleStruct(7, 0),
500            TupleStruct(-1, 12),
501            TupleStruct(18, 18),
502        ])
503        .unwrap();
504        let mut args = HashMap::new();
505        args.insert("attribute".to_string(), to_value("0").unwrap());
506
507        let result = sort(&v, &args);
508        assert!(result.is_ok());
509        assert_eq!(
510            result.unwrap(),
511            to_value(vec![
512                TupleStruct(-1, 12),
513                TupleStruct(0, 1),
514                TupleStruct(7, 0),
515                TupleStruct(18, 18),
516            ])
517            .unwrap()
518        );
519    }
520
521    #[test]
522    fn test_unique_numbers() {
523        let v = to_value(vec![3, -1, 3, 3, 5, 2, 5, 4]).unwrap();
524        let args = HashMap::new();
525        let result = unique(&v, &args);
526        assert!(result.is_ok());
527        assert_eq!(result.unwrap(), to_value(vec![3, -1, 5, 2, 4]).unwrap());
528    }
529
530    #[test]
531    fn test_unique_strings() {
532        let v = to_value(vec!["One", "Two", "Three", "one", "Two"]).unwrap();
533        let mut args = HashMap::new();
534        let result = unique(&v, &args);
535        assert!(result.is_ok());
536        assert_eq!(result.unwrap(), to_value(vec!["One", "Two", "Three"]).unwrap());
537
538        args.insert("case_sensitive".to_string(), to_value(true).unwrap());
539        let result = unique(&v, &args);
540        assert!(result.is_ok());
541        assert_eq!(result.unwrap(), to_value(vec!["One", "Two", "Three", "one"]).unwrap());
542    }
543
544    #[test]
545    fn test_unique_empty() {
546        let v = to_value(Vec::<f64>::new()).unwrap();
547        let args = HashMap::new();
548        let result = sort(&v, &args);
549        assert!(result.is_ok());
550        assert_eq!(result.unwrap(), to_value(Vec::<f64>::new()).unwrap());
551    }
552
553    #[test]
554    fn test_unique_attribute() {
555        let v = to_value(vec![
556            Foo { a: 1, b: 2 },
557            Foo { a: 3, b: 3 },
558            Foo { a: 1, b: 3 },
559            Foo { a: 0, b: 4 },
560        ])
561        .unwrap();
562        let mut args = HashMap::new();
563        args.insert("attribute".to_string(), to_value("a").unwrap());
564
565        let result = unique(&v, &args);
566        assert!(result.is_ok());
567        assert_eq!(
568            result.unwrap(),
569            to_value(vec![Foo { a: 1, b: 2 }, Foo { a: 3, b: 3 }, Foo { a: 0, b: 4 },]).unwrap()
570        );
571    }
572
573    #[test]
574    fn test_unique_invalid_attribute() {
575        let v = to_value(vec![Foo { a: 3, b: 5 }]).unwrap();
576        let mut args = HashMap::new();
577        args.insert("attribute".to_string(), to_value("invalid_field").unwrap());
578
579        let result = unique(&v, &args);
580        assert!(result.is_err());
581        assert_eq!(
582            result.unwrap_err().to_string(),
583            "attribute 'invalid_field' does not reference a field"
584        );
585    }
586
587    #[test]
588    fn test_unique_multiple_types() {
589        let v = to_value(vec![Value::Number(12.into()), Value::Array(vec![])]).unwrap();
590        let args = HashMap::new();
591
592        let result = unique(&v, &args);
593        assert!(result.is_err());
594        assert_eq!(result.unwrap_err().to_string(), "unique filter can't compare multiple types");
595    }
596
597    #[test]
598    fn test_unique_non_finite_numbers() {
599        let v = to_value(vec![
600            ::std::f64::NEG_INFINITY, // NaN and friends get deserialized as Null by serde.
601            ::std::f64::NAN,
602        ])
603        .unwrap();
604        let args = HashMap::new();
605
606        let result = unique(&v, &args);
607        assert!(result.is_err());
608        assert_eq!(result.unwrap_err().to_string(), "Null is not a unique value");
609    }
610
611    #[test]
612    fn test_unique_tuple() {
613        let v = to_value(vec![
614            TupleStruct(0, 1),
615            TupleStruct(-7, -1),
616            TupleStruct(-1, 1),
617            TupleStruct(18, 18),
618        ])
619        .unwrap();
620        let mut args = HashMap::new();
621        args.insert("attribute".to_string(), to_value("1").unwrap());
622
623        let result = unique(&v, &args);
624        assert!(result.is_ok());
625        assert_eq!(
626            result.unwrap(),
627            to_value(vec![TupleStruct(0, 1), TupleStruct(-7, -1), TupleStruct(18, 18),]).unwrap()
628        );
629    }
630
631    #[test]
632    fn test_slice() {
633        fn make_args(start: Option<usize>, end: Option<f64>) -> HashMap<String, Value> {
634            let mut args = HashMap::new();
635            if let Some(s) = start {
636                args.insert("start".to_string(), to_value(s).unwrap());
637            }
638            if let Some(e) = end {
639                args.insert("end".to_string(), to_value(e).unwrap());
640            }
641            args
642        }
643
644        let v = to_value(vec![1, 2, 3, 4, 5]).unwrap();
645
646        let inputs = vec![
647            (make_args(Some(1), None), vec![2, 3, 4, 5]),
648            (make_args(None, Some(2.0)), vec![1, 2]),
649            (make_args(Some(1), Some(2.0)), vec![2]),
650            (make_args(None, Some(-2.0)), vec![1, 2, 3]),
651            (make_args(None, None), vec![1, 2, 3, 4, 5]),
652            (make_args(Some(3), Some(1.0)), vec![]),
653            (make_args(Some(9), None), vec![]),
654        ];
655
656        for (args, expected) in inputs {
657            let res = slice(&v, &args);
658            assert!(res.is_ok());
659            assert_eq!(res.unwrap(), to_value(expected).unwrap());
660        }
661    }
662
663    #[test]
664    fn test_group_by() {
665        let input = json!([
666            {"id": 1, "year": 2015},
667            {"id": 2, "year": 2015},
668            {"id": 3, "year": 2016},
669            {"id": 4, "year": 2017},
670            {"id": 5, "year": 2017},
671            {"id": 6, "year": 2017},
672            {"id": 7, "year": 2018},
673            {"id": 8},
674            {"id": 9, "year": null},
675        ]);
676        let mut args = HashMap::new();
677        args.insert("attribute".to_string(), to_value("year").unwrap());
678
679        let expected = json!({
680            "2015": [{"id": 1, "year": 2015}, {"id": 2, "year": 2015}],
681            "2016": [{"id": 3, "year": 2016}],
682            "2017": [{"id": 4, "year": 2017}, {"id": 5, "year": 2017}, {"id": 6, "year": 2017}],
683            "2018": [{"id": 7, "year": 2018}],
684        });
685
686        let res = group_by(&input, &args);
687        assert!(res.is_ok());
688        assert_eq!(res.unwrap(), to_value(expected).unwrap());
689    }
690
691    #[test]
692    fn test_group_by_nested_key() {
693        let input = json!([
694            {"id": 1, "company": {"id": 1}},
695            {"id": 2, "company": {"id": 2}},
696            {"id": 3, "company": {"id": 3}},
697            {"id": 4, "company": {"id": 4}},
698            {"id": 5, "company": {"id": 4}},
699            {"id": 6, "company": {"id": 5}},
700            {"id": 7, "company": {"id": 5}},
701            {"id": 8},
702            {"id": 9, "company": null},
703        ]);
704        let mut args = HashMap::new();
705        args.insert("attribute".to_string(), to_value("company.id").unwrap());
706
707        let expected = json!({
708            "1": [{"id": 1, "company": {"id": 1}}],
709            "2": [{"id": 2, "company": {"id": 2}}],
710            "3": [{"id": 3, "company": {"id": 3}}],
711            "4": [{"id": 4, "company": {"id": 4}}, {"id": 5, "company": {"id": 4}}],
712            "5": [{"id": 6, "company": {"id": 5}}, {"id": 7, "company": {"id": 5}}],
713        });
714
715        let res = group_by(&input, &args);
716        assert!(res.is_ok());
717        assert_eq!(res.unwrap(), to_value(expected).unwrap());
718    }
719
720    #[test]
721    fn test_filter_empty() {
722        let res = filter(&json!([]), &HashMap::new());
723        assert!(res.is_ok());
724        assert_eq!(res.unwrap(), json!([]));
725    }
726
727    #[test]
728    fn test_filter() {
729        let input = json!([
730            {"id": 1, "year": 2015},
731            {"id": 2, "year": 2015},
732            {"id": 3, "year": 2016},
733            {"id": 4, "year": 2017},
734            {"id": 5, "year": 2017},
735            {"id": 6, "year": 2017},
736            {"id": 7, "year": 2018},
737            {"id": 8},
738            {"id": 9, "year": null},
739        ]);
740        let mut args = HashMap::new();
741        args.insert("attribute".to_string(), to_value("year").unwrap());
742        args.insert("value".to_string(), to_value(2015).unwrap());
743
744        let expected = json!([
745            {"id": 1, "year": 2015},
746            {"id": 2, "year": 2015},
747        ]);
748
749        let res = filter(&input, &args);
750        assert!(res.is_ok());
751        assert_eq!(res.unwrap(), to_value(expected).unwrap());
752    }
753
754    #[test]
755    fn test_filter_no_value() {
756        let input = json!([
757            {"id": 1, "year": 2015},
758            {"id": 2, "year": 2015},
759            {"id": 3, "year": 2016},
760            {"id": 4, "year": 2017},
761            {"id": 5, "year": 2017},
762            {"id": 6, "year": 2017},
763            {"id": 7, "year": 2018},
764            {"id": 8},
765            {"id": 9, "year": null},
766        ]);
767        let mut args = HashMap::new();
768        args.insert("attribute".to_string(), to_value("year").unwrap());
769
770        let expected = json!([
771            {"id": 1, "year": 2015},
772            {"id": 2, "year": 2015},
773            {"id": 3, "year": 2016},
774            {"id": 4, "year": 2017},
775            {"id": 5, "year": 2017},
776            {"id": 6, "year": 2017},
777            {"id": 7, "year": 2018},
778        ]);
779
780        let res = filter(&input, &args);
781        assert!(res.is_ok());
782        assert_eq!(res.unwrap(), to_value(expected).unwrap());
783    }
784
785    #[test]
786    fn test_map_empty() {
787        let res = map(&json!([]), &HashMap::new());
788        assert!(res.is_ok());
789        assert_eq!(res.unwrap(), json!([]));
790    }
791
792    #[test]
793    fn test_map() {
794        let input = json!([
795            {"id": 1, "year": 2015},
796            {"id": 2, "year": true},
797            {"id": 3, "year": 2016.5},
798            {"id": 4, "year": "2017"},
799            {"id": 5, "year": 2017},
800            {"id": 6, "year": 2017},
801            {"id": 7, "year": [1900, 1901]},
802            {"id": 8, "year": {"a": 2018, "b": 2019}},
803            {"id": 9},
804            {"id": 10, "year": null},
805        ]);
806        let mut args = HashMap::new();
807        args.insert("attribute".to_string(), to_value("year").unwrap());
808
809        let expected =
810            json!([2015, true, 2016.5, "2017", 2017, 2017, [1900, 1901], {"a": 2018, "b": 2019}]);
811
812        let res = map(&input, &args);
813        assert!(res.is_ok());
814        assert_eq!(res.unwrap(), to_value(expected).unwrap());
815    }
816
817    #[test]
818    fn test_concat_array() {
819        let input = json!([1, 2, 3,]);
820        let mut args = HashMap::new();
821        args.insert("with".to_string(), json!([3, 4]));
822        let expected = json!([1, 2, 3, 3, 4,]);
823
824        let res = concat(&input, &args);
825        assert!(res.is_ok());
826        assert_eq!(res.unwrap(), to_value(expected).unwrap());
827    }
828
829    #[test]
830    fn test_concat_single_value() {
831        let input = json!([1, 2, 3,]);
832        let mut args = HashMap::new();
833        args.insert("with".to_string(), json!(4));
834        let expected = json!([1, 2, 3, 4,]);
835
836        let res = concat(&input, &args);
837        assert!(res.is_ok());
838        assert_eq!(res.unwrap(), to_value(expected).unwrap());
839    }
840}