1use 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
12pub trait Function: Sync {
14 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult;
16}
17
18#[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 TypedArray(Box<ArgumentType>),
31 Union(Vec<ArgumentType>),
33}
34
35impl ArgumentType {
36 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
100pub struct CustomFunction {
102 signature: Signature,
104 f: Box<dyn Fn(&[Rcvar], &mut Context<'_>) -> SearchResult + Sync>,
106}
107
108impl CustomFunction {
109 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
128impl<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#[derive(Clone, PartialEq, Eq, Debug)]
144pub struct Signature {
145 pub inputs: Vec<ArgumentType>,
146 pub variadic: Option<ArgumentType>,
147}
148
149impl Signature {
150 pub fn new(inputs: Vec<ArgumentType>, variadic: Option<ArgumentType>) -> Signature {
152 Signature { inputs, variadic }
153 }
154
155 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 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
216macro_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
239macro_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 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 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 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
297macro_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 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}