1use 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
12pub trait Function: Sync + Send {
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
100type InvokedFunction = dyn Fn(&[Rcvar], &mut Context<'_>) -> SearchResult + Sync + Send;
101
102pub struct CustomFunction {
104 signature: Signature,
106 f: Box<InvokedFunction>,
108}
109
110impl CustomFunction {
111 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
127impl<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#[derive(Clone, PartialEq, Eq, Debug)]
143pub struct Signature {
144 pub inputs: Vec<ArgumentType>,
145 pub variadic: Option<ArgumentType>,
146}
147
148impl Signature {
149 pub fn new(inputs: Vec<ArgumentType>, variadic: Option<ArgumentType>) -> Signature {
151 Signature { inputs, variadic }
152 }
153
154 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 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
215macro_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
238macro_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 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 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 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
296macro_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 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}