jmespath/
functions.rs

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