jmespatch/
functions.rs

1//! JMESPath functions.
2
3use std::cmp::{max, min};
4use std::collections::BTreeMap;
5use std::fmt;
6
7use crate::interpreter::{interpret, SearchResult};
8use crate::variable::{JmespathType, Variable};
9use crate::{Context, ErrorReason, JmespathError, Rcvar, RuntimeError};
10use serde_json::Number;
11
12/// Represents a JMESPath function.
13pub trait Function: Sync {
14    /// Evaluates the function against an in-memory variable.
15    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult;
16}
17
18/// Function argument types used when validating.
19#[derive(Clone, PartialEq, Eq, Debug)]
20pub enum ArgumentType {
21    Any,
22    Null,
23    String,
24    Number,
25    Bool,
26    Object,
27    Array,
28    Expref,
29    /// Each element of the array must matched the provided type.
30    TypedArray(Box<ArgumentType>),
31    /// Accepts one of a number of `ArgumentType`s
32    Union(Vec<ArgumentType>),
33}
34
35impl ArgumentType {
36    /// Returns true/false if the variable is valid for the type.
37    pub fn is_valid(&self, value: &Rcvar) -> bool {
38        use self::ArgumentType::*;
39        match *self {
40            Any => true,
41            Null if value.is_null() => true,
42            String if value.is_string() => true,
43            Number if value.is_number() => true,
44            Object if value.is_object() => true,
45            Bool if value.is_boolean() => true,
46            Expref if value.is_expref() => true,
47            Array if value.is_array() => true,
48            TypedArray(ref t) if value.is_array() => {
49                if let Some(array) = value.as_array() {
50                    array.iter().all(|v| t.is_valid(v))
51                } else {
52                    false
53                }
54            }
55            Union(ref types) => types.iter().any(|t| t.is_valid(value)),
56            _ => false,
57        }
58    }
59}
60
61impl fmt::Display for ArgumentType {
62    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
63        use self::ArgumentType::*;
64        match *self {
65            Any => write!(fmt, "any"),
66            String => write!(fmt, "string"),
67            Number => write!(fmt, "number"),
68            Bool => write!(fmt, "boolean"),
69            Array => write!(fmt, "array"),
70            Object => write!(fmt, "object"),
71            Null => write!(fmt, "null"),
72            Expref => write!(fmt, "expref"),
73            TypedArray(ref t) => write!(fmt, "array[{}]", t),
74            Union(ref types) => {
75                let str_value = types
76                    .iter()
77                    .map(|t| t.to_string())
78                    .collect::<Vec<_>>()
79                    .join("|");
80                write!(fmt, "{}", str_value)
81            }
82        }
83    }
84}
85
86macro_rules! arg {
87    (any) => (ArgumentType::Any);
88    (null) => (ArgumentType::Null);
89    (string) => (ArgumentType::String);
90    (bool) => (ArgumentType::Bool);
91    (number) => (ArgumentType::Number);
92    (object) => (ArgumentType::Object);
93    (expref) => (ArgumentType::Expref);
94    (array_number) => (ArgumentType::TypedArray(Box::new(ArgumentType::Number)));
95    (array_string) => (ArgumentType::TypedArray(Box::new(ArgumentType::String)));
96    (array) => (ArgumentType::Array);
97    ($($x:ident) | *) => (ArgumentType::Union(vec![$(arg!($x)), *]));
98}
99
100/// Custom function that allows the creation of runtime functions with signature validation.
101pub struct CustomFunction {
102    /// Signature used to validate the function.
103    signature: Signature,
104    /// Function to invoke after validating the signature.
105    f: Box<dyn Fn(&[Rcvar], &mut Context<'_>) -> SearchResult + Sync>,
106}
107
108impl CustomFunction {
109    /// Creates a new custom function.
110    pub fn new(
111        fn_signature: Signature,
112        f: Box<dyn Fn(&[Rcvar], &mut Context<'_>) -> SearchResult + Sync>,
113    ) -> CustomFunction {
114        CustomFunction {
115            signature: fn_signature,
116            f,
117        }
118    }
119}
120
121impl Function for CustomFunction {
122    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
123        self.signature.validate(args, ctx)?;
124        (self.f)(args, ctx)
125    }
126}
127
128/// Normal closures can be used as functions.
129///
130/// It is up to the function to validate the provided arguments.
131/// If you wish to utilize Signatures or more complex argument
132/// validation, it is recommended to use CustomFunction.
133impl<F> Function for F
134where
135    F: Sync + Fn(&[Rcvar], &mut Context<'_>) -> SearchResult,
136{
137    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
138        (self)(args, ctx)
139    }
140}
141
142/// Represents a function's signature.
143#[derive(Clone, PartialEq, Eq, Debug)]
144pub struct Signature {
145    pub inputs: Vec<ArgumentType>,
146    pub variadic: Option<ArgumentType>,
147}
148
149impl Signature {
150    /// Creates a new Signature struct.
151    pub fn new(inputs: Vec<ArgumentType>, variadic: Option<ArgumentType>) -> Signature {
152        Signature { inputs, variadic }
153    }
154
155    /// Validates the arity of a function. If the arity is invalid, a runtime
156    /// error is returned with the relative position of the error and the
157    /// expression that was being executed.
158    pub fn validate_arity(&self, actual: usize, ctx: &Context<'_>) -> Result<(), JmespathError> {
159        let expected = self.inputs.len();
160        if self.variadic.is_some() {
161            if actual >= expected {
162                Ok(())
163            } else {
164                let reason =
165                    ErrorReason::Runtime(RuntimeError::NotEnoughArguments { expected, actual });
166                Err(JmespathError::from_ctx(ctx, reason))
167            }
168        } else if actual == expected {
169            Ok(())
170        } else if actual < expected {
171            let reason =
172                ErrorReason::Runtime(RuntimeError::NotEnoughArguments { expected, actual });
173            Err(JmespathError::from_ctx(ctx, reason))
174        } else {
175            let reason = ErrorReason::Runtime(RuntimeError::TooManyArguments { expected, actual });
176            Err(JmespathError::from_ctx(ctx, reason))
177        }
178    }
179
180    /// Validates the provided function arguments against the signature.
181    pub fn validate(&self, args: &[Rcvar], ctx: &Context<'_>) -> Result<(), JmespathError> {
182        self.validate_arity(args.len(), ctx)?;
183        if let Some(ref variadic) = self.variadic {
184            for (k, v) in args.iter().enumerate() {
185                let validator = self.inputs.get(k).unwrap_or(variadic);
186                self.validate_arg(ctx, k, v, validator)?;
187            }
188        } else {
189            for (k, v) in args.iter().enumerate() {
190                self.validate_arg(ctx, k, v, &self.inputs[k])?;
191            }
192        }
193        Ok(())
194    }
195
196    fn validate_arg(
197        &self,
198        ctx: &Context<'_>,
199        position: usize,
200        value: &Rcvar,
201        validator: &ArgumentType,
202    ) -> Result<(), JmespathError> {
203        if validator.is_valid(value) {
204            Ok(())
205        } else {
206            let reason = ErrorReason::Runtime(RuntimeError::InvalidType {
207                expected: validator.to_string(),
208                actual: value.get_type().to_string(),
209                position,
210            });
211            Err(JmespathError::from_ctx(ctx, reason))
212        }
213    }
214}
215
216/// Macro to more easily and quickly define a function and signature.
217macro_rules! defn {
218    ($name:ident, $args:expr, $variadic:expr) => {
219        pub struct $name {
220            signature: Signature,
221        }
222
223        impl Default for $name {
224            fn default() -> Self {
225                Self::new()
226            }
227        }
228
229        impl $name {
230            pub fn new() -> $name {
231                $name {
232                    signature: Signature::new($args, $variadic),
233                }
234            }
235        }
236    };
237}
238
239/// Macro used to implement max_by and min_by functions.
240macro_rules! min_and_max_by {
241    ($ctx:expr, $operator:ident, $args:expr) => {{
242        let vals = $args[0].as_array().ok_or_else(|| {
243            JmespathError::new(
244                "",
245                0,
246                ErrorReason::Parse("Expected args[0] to be an array".to_owned()),
247            )
248        })?;
249        // Return null when there are not values in the array
250        if vals.is_empty() {
251            return Ok(Rcvar::new(Variable::Null));
252        }
253        let ast = $args[1].as_expref().ok_or_else(|| {
254            JmespathError::new(
255                "",
256                0,
257                ErrorReason::Parse("Expected args[1] to be an expression".to_owned()),
258            )
259        })?;
260        // Map over the first value to get the homogeneous required return type
261        let initial = interpret(&vals[0], &ast, $ctx)?;
262        let entered_type = initial.get_type();
263        if entered_type != JmespathType::String && entered_type != JmespathType::Number {
264            return Err(JmespathError::from_ctx(
265                $ctx,
266                ErrorReason::Runtime(RuntimeError::InvalidReturnType {
267                    expected: "expression->number|expression->string".to_owned(),
268                    actual: entered_type.to_string(),
269                    position: 1,
270                    invocation: 1,
271                }),
272            ));
273        }
274        // Map over each value, finding the best candidate value and fail on error.
275        let mut candidate = (vals[0].clone(), initial.clone());
276        for (invocation, v) in vals.iter().enumerate().skip(1) {
277            let mapped = interpret(v, &ast, $ctx)?;
278            if mapped.get_type() != entered_type {
279                return Err(JmespathError::from_ctx(
280                    $ctx,
281                    ErrorReason::Runtime(RuntimeError::InvalidReturnType {
282                        expected: format!("expression->{}", entered_type),
283                        actual: mapped.get_type().to_string(),
284                        position: 1,
285                        invocation: invocation,
286                    }),
287                ));
288            }
289            if mapped.$operator(&candidate.1) {
290                candidate = (v.clone(), mapped);
291            }
292        }
293        Ok(candidate.0)
294    }};
295}
296
297/// Macro used to implement max and min functions.
298macro_rules! min_and_max {
299    ($operator:ident, $args:expr) => {{
300        let values = $args[0].as_array().ok_or_else(|| {
301            JmespathError::new(
302                "",
303                0,
304                ErrorReason::Parse("Expected args[0] to be an array".to_owned()),
305            )
306        })?;
307        if values.is_empty() {
308            Ok(Rcvar::new(Variable::Null))
309        } else {
310            let result: Rcvar = values
311                .iter()
312                .skip(1)
313                .fold(values[0].clone(), |acc, item| $operator(acc, item.clone()));
314            Ok(result)
315        }
316    }};
317}
318
319defn!(AbsFn, vec![arg!(number)], None);
320
321impl Function for AbsFn {
322    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
323        self.signature.validate(args, ctx)?;
324        match args[0].as_ref() {
325            Variable::Number(n) => Ok(Rcvar::new(Variable::Number(
326                Number::from_f64(
327                    n.as_f64()
328                        .ok_or_else(|| {
329                            JmespathError::new(
330                                "",
331                                0,
332                                ErrorReason::Parse("Expected to be a valid f64".to_owned()),
333                            )
334                        })?
335                        .abs(),
336                )
337                .ok_or_else(|| {
338                    JmespathError::new(
339                        "",
340                        0,
341                        ErrorReason::Parse("Expected to be a valid f64".to_owned()),
342                    )
343                })?,
344            ))),
345            _ => Ok(args[0].clone()),
346        }
347    }
348}
349
350defn!(AvgFn, vec![arg!(array_number)], None);
351
352impl Function for AvgFn {
353    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
354        self.signature.validate(args, ctx)?;
355        let values = args[0].as_array().ok_or_else(|| {
356            JmespathError::new(
357                "",
358                0,
359                ErrorReason::Parse("Expected args[0] to be an array".to_owned()),
360            )
361        })?;
362
363        let mut sum = 0.0;
364
365        for value in values {
366            sum += value.as_number().ok_or_else(|| {
367                JmespathError::new(
368                    "",
369                    0,
370                    ErrorReason::Parse("Expected to be a valid f64".to_owned()),
371                )
372            })?;
373        }
374
375        Ok(Rcvar::new(Variable::Number(
376            Number::from_f64(sum / (values.len() as f64)).ok_or_else(|| {
377                JmespathError::new(
378                    "",
379                    0,
380                    ErrorReason::Parse("Expected to be a valid f64".to_owned()),
381                )
382            })?,
383        )))
384    }
385}
386
387defn!(CeilFn, vec![arg!(number)], None);
388
389impl Function for CeilFn {
390    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
391        self.signature.validate(args, ctx)?;
392        let n = args[0].as_number().ok_or_else(|| {
393            JmespathError::new(
394                "",
395                0,
396                ErrorReason::Parse("Expected args[0] to be a number".to_owned()),
397            )
398        })?;
399        Ok(Rcvar::new(Variable::Number(
400            Number::from_f64(n.ceil()).ok_or_else(|| {
401                JmespathError::new(
402                    "",
403                    0,
404                    ErrorReason::Parse("Expected n.ceil() to be a valid f64".to_owned()),
405                )
406            })?,
407        )))
408    }
409}
410
411defn!(ContainsFn, vec![arg!(string | array), arg!(any)], None);
412
413impl Function for ContainsFn {
414    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
415        self.signature.validate(args, ctx)?;
416        let haystack = &args[0];
417        let needle = &args[1];
418        match **haystack {
419            Variable::Array(ref a) => Ok(Rcvar::new(Variable::Bool(a.contains(&needle)))),
420            Variable::String(ref subj) => match needle.as_string() {
421                None => Ok(Rcvar::new(Variable::Bool(false))),
422                Some(s) => Ok(Rcvar::new(Variable::Bool(subj.contains(s)))),
423            },
424            _ => unreachable!(),
425        }
426    }
427}
428
429defn!(EndsWithFn, vec![arg!(string), arg!(string)], None);
430
431impl Function for EndsWithFn {
432    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
433        self.signature.validate(args, ctx)?;
434        let subject = args[0].as_string().ok_or_else(|| {
435            JmespathError::new(
436                "",
437                0,
438                ErrorReason::Parse("Expected args[0] to be a valid string".to_owned()),
439            )
440        })?;
441        let search = args[1].as_string().ok_or_else(|| {
442            JmespathError::new(
443                "",
444                0,
445                ErrorReason::Parse("Expected args[1] to be a valid string".to_owned()),
446            )
447        })?;
448        Ok(Rcvar::new(Variable::Bool(subject.ends_with(search))))
449    }
450}
451
452defn!(FloorFn, vec![arg!(number)], None);
453
454impl Function for FloorFn {
455    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
456        self.signature.validate(args, ctx)?;
457        let n = args[0].as_number().ok_or_else(|| {
458            JmespathError::new(
459                "",
460                0,
461                ErrorReason::Parse("Expected args[0] to be a valid number".to_owned()),
462            )
463        })?;
464        Ok(Rcvar::new(Variable::Number(
465            Number::from_f64(n.floor()).ok_or_else(|| {
466                JmespathError::new(
467                    "",
468                    0,
469                    ErrorReason::Parse("Expected to be a valid number".to_owned()),
470                )
471            })?,
472        )))
473    }
474}
475
476defn!(JoinFn, vec![arg!(string), arg!(array_string)], None);
477
478impl Function for JoinFn {
479    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
480        self.signature.validate(args, ctx)?;
481        let glue = args[0].as_string().ok_or_else(|| {
482            JmespathError::new(
483                "",
484                0,
485                ErrorReason::Parse("Expected args[0] to be a valid string".to_owned()),
486            )
487        })?;
488        let values = args[1].as_array().ok_or_else(|| {
489            JmespathError::new(
490                "",
491                0,
492                ErrorReason::Parse("Expected args[1] to be a valid string".to_owned()),
493            )
494        })?;
495        let result = values
496            .iter()
497            .map(|v| {
498                v.as_string().map(|val| val.to_owned()).ok_or_else(|| {
499                    JmespathError::new(
500                        "",
501                        0,
502                        ErrorReason::Parse("Expected to be a valid string".to_owned()),
503                    )
504                })
505            })
506            .collect::<Result<Vec<String>, JmespathError>>()?
507            .join(&glue);
508        Ok(Rcvar::new(Variable::String(result)))
509    }
510}
511
512defn!(KeysFn, vec![arg!(object)], None);
513
514impl Function for KeysFn {
515    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
516        self.signature.validate(args, ctx)?;
517        let object = args[0].as_object().ok_or_else(|| {
518            JmespathError::new(
519                "",
520                0,
521                ErrorReason::Parse("Expected args[0] to be a valid Object".to_owned()),
522            )
523        })?;
524        let keys = object
525            .keys()
526            .map(|k| Rcvar::new(Variable::String((*k).clone())))
527            .collect::<Vec<Rcvar>>();
528        Ok(Rcvar::new(Variable::Array(keys)))
529    }
530}
531
532defn!(LengthFn, vec![arg!(array | object | string)], None);
533
534impl Function for LengthFn {
535    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
536        self.signature.validate(args, ctx)?;
537        match args[0].as_ref() {
538            Variable::Array(ref a) => Ok(Rcvar::new(Variable::Number(Number::from(a.len())))),
539            Variable::Object(ref m) => Ok(Rcvar::new(Variable::Number(Number::from(m.len())))),
540            // Note that we need to count the code points not the number of unicode characters
541            Variable::String(ref s) => Ok(Rcvar::new(Variable::Number(Number::from(
542                s.chars().count(),
543            )))),
544            _ => unreachable!(),
545        }
546    }
547}
548
549defn!(MapFn, vec![arg!(expref), arg!(array)], None);
550
551impl Function for MapFn {
552    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
553        self.signature.validate(args, ctx)?;
554        let ast = args[0].as_expref().ok_or_else(|| {
555            JmespathError::new(
556                "",
557                0,
558                ErrorReason::Parse("Expected args[0] to be an expref".to_owned()),
559            )
560        })?;
561        let values = args[1].as_array().ok_or_else(|| {
562            JmespathError::new(
563                "",
564                0,
565                ErrorReason::Parse("Expected args[1] to be an array".to_owned()),
566            )
567        })?;
568        let mut results = vec![];
569        for value in values {
570            results.push(interpret(&value, &ast, ctx)?);
571        }
572        Ok(Rcvar::new(Variable::Array(results)))
573    }
574}
575
576defn!(MaxFn, vec![arg!(array_string | array_number)], None);
577
578impl Function for MaxFn {
579    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
580        self.signature.validate(args, ctx)?;
581        min_and_max!(max, args)
582    }
583}
584
585defn!(MinFn, vec![arg!(array_string | array_number)], None);
586
587impl Function for MinFn {
588    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
589        self.signature.validate(args, ctx)?;
590        min_and_max!(min, args)
591    }
592}
593
594defn!(MaxByFn, vec![arg!(array), arg!(expref)], None);
595
596impl Function for MaxByFn {
597    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
598        self.signature.validate(args, ctx)?;
599        min_and_max_by!(ctx, gt, args)
600    }
601}
602
603defn!(MinByFn, vec![arg!(array), arg!(expref)], None);
604
605impl Function for MinByFn {
606    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
607        self.signature.validate(args, ctx)?;
608        min_and_max_by!(ctx, lt, args)
609    }
610}
611
612defn!(MergeFn, vec![arg!(object)], Some(arg!(object)));
613
614impl Function for MergeFn {
615    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
616        self.signature.validate(args, ctx)?;
617        let mut result = BTreeMap::new();
618        for arg in args {
619            result.extend(
620                arg.as_object()
621                    .ok_or_else(|| {
622                        JmespathError::new(
623                            "",
624                            0,
625                            ErrorReason::Parse("Expected to be a valid Object".to_owned()),
626                        )
627                    })?
628                    .clone(),
629            );
630        }
631        Ok(Rcvar::new(Variable::Object(result)))
632    }
633}
634
635defn!(NotNullFn, vec![arg!(any)], Some(arg!(any)));
636
637impl Function for NotNullFn {
638    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
639        self.signature.validate(args, ctx)?;
640        for arg in args {
641            if !arg.is_null() {
642                return Ok(arg.clone());
643            }
644        }
645        Ok(Rcvar::new(Variable::Null))
646    }
647}
648
649defn!(ReverseFn, vec![arg!(array | string)], None);
650
651impl Function for ReverseFn {
652    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
653        self.signature.validate(args, ctx)?;
654        if args[0].is_array() {
655            let mut values = args[0]
656                .as_array()
657                .ok_or_else(|| {
658                    JmespathError::new(
659                        "",
660                        0,
661                        ErrorReason::Parse("Expected args[0] to be an array".to_owned()),
662                    )
663                })?
664                .clone();
665            values.reverse();
666            Ok(Rcvar::new(Variable::Array(values)))
667        } else {
668            let word: String = args[0]
669                .as_string()
670                .ok_or_else(|| {
671                    JmespathError::new(
672                        "",
673                        0,
674                        ErrorReason::Parse("Expected args[0] to be a string".to_owned()),
675                    )
676                })?
677                .chars()
678                .rev()
679                .collect();
680            Ok(Rcvar::new(Variable::String(word)))
681        }
682    }
683}
684
685defn!(SortFn, vec![arg!(array_string | array_number)], None);
686
687impl Function for SortFn {
688    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
689        self.signature.validate(args, ctx)?;
690        let mut values = args[0]
691            .as_array()
692            .ok_or_else(|| {
693                JmespathError::new(
694                    "",
695                    0,
696                    ErrorReason::Parse("Expected args[0] to be an array".to_owned()),
697                )
698            })?
699            .clone();
700        values.sort();
701        Ok(Rcvar::new(Variable::Array(values)))
702    }
703}
704
705defn!(SortByFn, vec![arg!(array), arg!(expref)], None);
706
707impl Function for SortByFn {
708    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
709        self.signature.validate(args, ctx)?;
710        let vals = args[0]
711            .as_array()
712            .ok_or_else(|| {
713                JmespathError::new(
714                    "",
715                    0,
716                    ErrorReason::Parse("Expected args[0] to be an array".to_owned()),
717                )
718            })?
719            .clone();
720        if vals.is_empty() {
721            return Ok(Rcvar::new(Variable::Array(vals)));
722        }
723        let ast = args[1].as_expref().ok_or_else(|| {
724            JmespathError::new(
725                "",
726                0,
727                ErrorReason::Parse("Expected args[1] to be an expref".to_owned()),
728            )
729        })?;
730        let mut mapped: Vec<(Rcvar, Rcvar)> = vec![];
731        let first_value = interpret(&vals[0], &ast, ctx)?;
732        let first_type = first_value.get_type();
733        if first_type != JmespathType::String && first_type != JmespathType::Number {
734            let reason = ErrorReason::Runtime(RuntimeError::InvalidReturnType {
735                expected: "expression->string|expression->number".to_owned(),
736                actual: first_type.to_string(),
737                position: 1,
738                invocation: 1,
739            });
740            return Err(JmespathError::from_ctx(ctx, reason));
741        }
742        mapped.push((vals[0].clone(), first_value));
743        for (invocation, v) in vals.iter().enumerate().skip(1) {
744            let mapped_value = interpret(v, &ast, ctx)?;
745            if mapped_value.get_type() != first_type {
746                return Err(JmespathError::from_ctx(
747                    ctx,
748                    ErrorReason::Runtime(RuntimeError::InvalidReturnType {
749                        expected: format!("expression->{}", first_type),
750                        actual: mapped_value.get_type().to_string(),
751                        position: 1,
752                        invocation,
753                    }),
754                ));
755            }
756            mapped.push((v.clone(), mapped_value));
757        }
758        mapped.sort_by(|a, b| a.1.cmp(&b.1));
759        let result = mapped.iter().map(|tuple| tuple.0.clone()).collect();
760        Ok(Rcvar::new(Variable::Array(result)))
761    }
762}
763
764defn!(StartsWithFn, vec![arg!(string), arg!(string)], None);
765
766impl Function for StartsWithFn {
767    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
768        self.signature.validate(args, ctx)?;
769        let subject = args[0].as_string().ok_or_else(|| {
770            JmespathError::new(
771                "",
772                0,
773                ErrorReason::Parse("Expected args[0] to be a string".to_owned()),
774            )
775        })?;
776        let search = args[1].as_string().ok_or_else(|| {
777            JmespathError::new(
778                "",
779                0,
780                ErrorReason::Parse("Expected args[1] to be a string".to_owned()),
781            )
782        })?;
783        Ok(Rcvar::new(Variable::Bool(subject.starts_with(search))))
784    }
785}
786
787defn!(SumFn, vec![arg!(array_number)], None);
788
789impl Function for SumFn {
790    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
791        self.signature.validate(args, ctx)?;
792        let result = args[0]
793            .as_array()
794            .ok_or_else(|| {
795                JmespathError::new(
796                    "",
797                    0,
798                    ErrorReason::Parse("Expected args[0] to be an array".to_owned()),
799                )
800            })?
801            .iter()
802            .fold(0.0, |acc, item| acc + item.as_number().unwrap_or(0.0));
803        Ok(Rcvar::new(Variable::Number(
804            Number::from_f64(result).ok_or_else(|| {
805                JmespathError::new(
806                    "",
807                    0,
808                    ErrorReason::Parse("Expected to be a valid number".to_owned()),
809                )
810            })?,
811        )))
812    }
813}
814
815defn!(ToArrayFn, vec![arg!(any)], None);
816
817impl Function for ToArrayFn {
818    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
819        self.signature.validate(args, ctx)?;
820        match *args[0] {
821            Variable::Array(_) => Ok(args[0].clone()),
822            _ => Ok(Rcvar::new(Variable::Array(vec![args[0].clone()]))),
823        }
824    }
825}
826
827defn!(ToNumberFn, vec![arg!(any)], None);
828
829impl Function for ToNumberFn {
830    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
831        self.signature.validate(args, ctx)?;
832        match *args[0] {
833            Variable::Number(_) => Ok(args[0].clone()),
834            Variable::String(ref s) => match Variable::from_json(s) {
835                Ok(f) => Ok(Rcvar::new(f)),
836                Err(_) => Ok(Rcvar::new(Variable::Null)),
837            },
838            _ => Ok(Rcvar::new(Variable::Null)),
839        }
840    }
841}
842
843defn!(
844    ToStringFn,
845    vec![arg!(object | array | bool | number | string | null)],
846    None
847);
848
849impl Function for ToStringFn {
850    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
851        self.signature.validate(args, ctx)?;
852        match *args[0] {
853            Variable::String(_) => Ok(args[0].clone()),
854            _ => Ok(Rcvar::new(Variable::String(args[0].to_string()))),
855        }
856    }
857}
858
859defn!(TypeFn, vec![arg!(any)], None);
860
861impl Function for TypeFn {
862    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
863        self.signature.validate(args, ctx)?;
864        Ok(Rcvar::new(Variable::String(args[0].get_type().to_string())))
865    }
866}
867
868defn!(ValuesFn, vec![arg!(object)], None);
869
870impl Function for ValuesFn {
871    fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
872        self.signature.validate(args, ctx)?;
873        let map = args[0].as_object().ok_or_else(|| {
874            JmespathError::new(
875                "",
876                0,
877                ErrorReason::Parse("Expected args[1] to be an Object".to_owned()),
878            )
879        })?;
880        Ok(Rcvar::new(Variable::Array(
881            map.values().cloned().collect::<Vec<Rcvar>>(),
882        )))
883    }
884}