1use std::fmt;
4
5use serde_json::{Number, Value};
6
7use crate::ast::Ast;
8use crate::interpreter::{SearchResult, interpret};
9use crate::value_ext::{JmespathType, ValueExt};
10use crate::{Context, ErrorReason, JmespathError, RuntimeError, get_expref_id};
11
12pub trait Function: Sync + Send {
14 fn evaluate(&self, args: &[Value], 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: &Value) -> 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() && !value.is_expref() => 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
86#[macro_export]
87macro_rules! arg {
88 (any) => ($crate::functions::ArgumentType::Any);
89 (null) => ($crate::functions::ArgumentType::Null);
90 (string) => ($crate::functions::ArgumentType::String);
91 (bool) => ($crate::functions::ArgumentType::Bool);
92 (number) => ($crate::functions::ArgumentType::Number);
93 (object) => ($crate::functions::ArgumentType::Object);
94 (expref) => ($crate::functions::ArgumentType::Expref);
95 (array_number) => ($crate::functions::ArgumentType::TypedArray(Box::new($crate::functions::ArgumentType::Number)));
96 (array_string) => ($crate::functions::ArgumentType::TypedArray(Box::new($crate::functions::ArgumentType::String)));
97 (array) => ($crate::functions::ArgumentType::Array);
98 ($($x:ident) | *) => ($crate::functions::ArgumentType::Union(vec![$($crate::arg!($x)), *]));
99}
100
101type InvokedFunction = dyn Fn(&[Value], &mut Context<'_>) -> SearchResult + Sync + Send;
102
103pub struct CustomFunction {
105 signature: Signature,
106 f: Box<InvokedFunction>,
107}
108
109impl CustomFunction {
110 pub fn new(fn_signature: Signature, f: Box<InvokedFunction>) -> CustomFunction {
112 CustomFunction {
113 signature: fn_signature,
114 f,
115 }
116 }
117}
118
119impl Function for CustomFunction {
120 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
121 self.signature.validate(args, ctx)?;
122 (self.f)(args, ctx)
123 }
124}
125
126impl<F> Function for F
128where
129 F: Send + Sync + Fn(&[Value], &mut Context<'_>) -> SearchResult,
130{
131 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
132 (self)(args, ctx)
133 }
134}
135
136#[derive(Clone, PartialEq, Eq, Debug)]
138pub struct Signature {
139 pub inputs: Vec<ArgumentType>,
140 pub variadic: Option<ArgumentType>,
141}
142
143impl Signature {
144 pub fn new(inputs: Vec<ArgumentType>, variadic: Option<ArgumentType>) -> Signature {
146 Signature { inputs, variadic }
147 }
148
149 pub fn validate_arity(&self, actual: usize, ctx: &Context<'_>) -> Result<(), JmespathError> {
151 let expected = self.inputs.len();
152 if self.variadic.is_some() {
153 if actual >= expected {
154 Ok(())
155 } else {
156 let reason =
157 ErrorReason::Runtime(RuntimeError::NotEnoughArguments { expected, actual });
158 Err(JmespathError::from_ctx(ctx, reason))
159 }
160 } else if actual == expected {
161 Ok(())
162 } else if actual < expected {
163 let reason =
164 ErrorReason::Runtime(RuntimeError::NotEnoughArguments { expected, actual });
165 Err(JmespathError::from_ctx(ctx, reason))
166 } else {
167 let reason = ErrorReason::Runtime(RuntimeError::TooManyArguments { expected, actual });
168 Err(JmespathError::from_ctx(ctx, reason))
169 }
170 }
171
172 pub fn validate(&self, args: &[Value], ctx: &Context<'_>) -> Result<(), JmespathError> {
174 self.validate_arity(args.len(), ctx)?;
175 if let Some(ref variadic) = self.variadic {
176 for (k, v) in args.iter().enumerate() {
177 let validator = self.inputs.get(k).unwrap_or(variadic);
178 self.validate_arg(ctx, k, v, validator)?;
179 }
180 } else {
181 for (k, v) in args.iter().enumerate() {
182 self.validate_arg(ctx, k, v, &self.inputs[k])?;
183 }
184 }
185 Ok(())
186 }
187
188 fn validate_arg(
189 &self,
190 ctx: &Context<'_>,
191 position: usize,
192 value: &Value,
193 validator: &ArgumentType,
194 ) -> Result<(), JmespathError> {
195 if validator.is_valid(value) {
196 Ok(())
197 } else {
198 let reason = ErrorReason::Runtime(RuntimeError::InvalidType {
199 expected: validator.to_string(),
200 actual: value.jmespath_type().to_string(),
201 position,
202 });
203 Err(JmespathError::from_ctx(ctx, reason))
204 }
205 }
206}
207
208fn get_expref_ast<'a>(value: &Value, ctx: &'a Context<'_>) -> Option<&'a Ast> {
210 get_expref_id(value).and_then(|id| ctx.get_expref(id))
211}
212
213pub fn number_value(n: f64) -> Value {
215 Number::from_f64(n).map_or(Value::Null, Value::Number)
216}
217
218pub fn custom_error(ctx: &Context<'_>, message: &str) -> JmespathError {
220 JmespathError::from_ctx(ctx, ErrorReason::Parse(message.to_owned()))
221}
222
223pub fn invalid_type_error(
225 ctx: &Context<'_>,
226 position: usize,
227 expected: &str,
228 actual: &Value,
229) -> JmespathError {
230 JmespathError::from_ctx(
231 ctx,
232 ErrorReason::Runtime(RuntimeError::InvalidType {
233 expected: expected.to_owned(),
234 actual: actual.jmespath_type().to_string(),
235 position,
236 }),
237 )
238}
239
240#[macro_export]
241macro_rules! defn {
242 ($name:ident, $args:expr, $variadic:expr) => {
243 pub struct $name {
244 signature: $crate::functions::Signature,
245 }
246
247 impl Default for $name {
248 fn default() -> Self {
249 Self::new()
250 }
251 }
252
253 impl $name {
254 pub fn new() -> $name {
255 $name {
256 signature: $crate::functions::Signature::new($args, $variadic),
257 }
258 }
259 }
260 };
261}
262
263defn!(AbsFn, vec![arg!(number)], None);
266
267impl Function for AbsFn {
268 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
269 self.signature.validate(args, ctx)?;
270 let n = args[0].as_f64().unwrap();
271 Ok(number_value(n.abs()))
272 }
273}
274
275defn!(AvgFn, vec![arg!(array_number)], None);
276
277impl Function for AvgFn {
278 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
279 self.signature.validate(args, ctx)?;
280 let arr = args[0].as_array().unwrap();
281 if arr.is_empty() {
282 return Ok(Value::Null);
283 }
284 let sum: f64 = arr.iter().map(|v| v.as_f64().unwrap_or(0.0)).sum();
285 Ok(number_value(sum / arr.len() as f64))
286 }
287}
288
289defn!(CeilFn, vec![arg!(number)], None);
290
291impl Function for CeilFn {
292 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
293 self.signature.validate(args, ctx)?;
294 let n = args[0].as_f64().unwrap();
295 Ok(number_value(n.ceil()))
296 }
297}
298
299defn!(ContainsFn, vec![arg!(string | array), arg!(any)], None);
300
301impl Function for ContainsFn {
302 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
303 self.signature.validate(args, ctx)?;
304 match &args[0] {
305 Value::Array(arr) => Ok(Value::Bool(arr.contains(&args[1]))),
306 Value::String(s) => match args[1].as_str() {
307 Some(needle) => Ok(Value::Bool(s.contains(needle))),
308 None => Ok(Value::Bool(false)),
309 },
310 _ => unreachable!(),
311 }
312 }
313}
314
315defn!(EndsWithFn, vec![arg!(string), arg!(string)], None);
316
317impl Function for EndsWithFn {
318 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
319 self.signature.validate(args, ctx)?;
320 let subject = args[0].as_str().unwrap();
321 let search = args[1].as_str().unwrap();
322 Ok(Value::Bool(subject.ends_with(search)))
323 }
324}
325
326defn!(FloorFn, vec![arg!(number)], None);
327
328impl Function for FloorFn {
329 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
330 self.signature.validate(args, ctx)?;
331 let n = args[0].as_f64().unwrap();
332 Ok(number_value(n.floor()))
333 }
334}
335
336defn!(JoinFn, vec![arg!(string), arg!(array_string)], None);
337
338impl Function for JoinFn {
339 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
340 self.signature.validate(args, ctx)?;
341 let glue = args[0].as_str().unwrap();
342 let values = args[1].as_array().unwrap();
343 let result: String = values
344 .iter()
345 .map(|v| v.as_str().unwrap().to_owned())
346 .collect::<Vec<String>>()
347 .join(glue);
348 Ok(Value::String(result))
349 }
350}
351
352defn!(KeysFn, vec![arg!(object)], None);
353
354impl Function for KeysFn {
355 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
356 self.signature.validate(args, ctx)?;
357 let obj = args[0].as_object().unwrap();
358 let keys: Vec<Value> = obj.keys().map(|k| Value::String(k.clone())).collect();
359 Ok(Value::Array(keys))
360 }
361}
362
363defn!(LengthFn, vec![arg!(array | object | string)], None);
364
365impl Function for LengthFn {
366 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
367 self.signature.validate(args, ctx)?;
368 let len = match &args[0] {
369 Value::Array(a) => a.len(),
370 Value::Object(m) => m.len(),
371 Value::String(s) => s.chars().count(),
372 _ => unreachable!(),
373 };
374 Ok(Value::Number(Number::from(len)))
375 }
376}
377
378defn!(MapFn, vec![arg!(expref), arg!(array)], None);
379
380impl Function for MapFn {
381 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
382 self.signature.validate(args, ctx)?;
383 let ast = get_expref_ast(&args[0], ctx).ok_or_else(|| {
384 JmespathError::new("", 0, ErrorReason::Parse("Expected expref".to_owned()))
385 })?;
386 let ast = ast.clone();
388 let values = args[1].as_array().unwrap();
389 let mut results = vec![];
390 for value in values {
391 results.push(interpret(value, &ast, ctx)?);
392 }
393 Ok(Value::Array(results))
394 }
395}
396
397defn!(MaxFn, vec![arg!(array_string | array_number)], None);
398
399impl Function for MaxFn {
400 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
401 self.signature.validate(args, ctx)?;
402 let values = args[0].as_array().unwrap();
403 if values.is_empty() {
404 return Ok(Value::Null);
405 }
406 let result = values
407 .iter()
408 .skip(1)
409 .fold(values[0].clone(), |acc, item| max_value(acc, item.clone()));
410 Ok(result)
411 }
412}
413
414defn!(MinFn, vec![arg!(array_string | array_number)], None);
415
416impl Function for MinFn {
417 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
418 self.signature.validate(args, ctx)?;
419 let values = args[0].as_array().unwrap();
420 if values.is_empty() {
421 return Ok(Value::Null);
422 }
423 let result = values
424 .iter()
425 .skip(1)
426 .fold(values[0].clone(), |acc, item| min_value(acc, item.clone()));
427 Ok(result)
428 }
429}
430
431defn!(MaxByFn, vec![arg!(array), arg!(expref)], None);
432
433impl Function for MaxByFn {
434 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
435 self.signature.validate(args, ctx)?;
436 min_max_by(ctx, &args[0], &args[1], |a, b| compare_values(a, b).is_gt())
437 }
438}
439
440defn!(MinByFn, vec![arg!(array), arg!(expref)], None);
441
442impl Function for MinByFn {
443 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
444 self.signature.validate(args, ctx)?;
445 min_max_by(ctx, &args[0], &args[1], |a, b| compare_values(a, b).is_lt())
446 }
447}
448
449defn!(MergeFn, vec![arg!(object)], Some(arg!(object)));
450
451impl Function for MergeFn {
452 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
453 self.signature.validate(args, ctx)?;
454 let mut result = serde_json::Map::new();
455 for arg in args {
456 if let Some(obj) = arg.as_object() {
457 result.extend(obj.clone());
458 }
459 }
460 Ok(Value::Object(result))
461 }
462}
463
464defn!(NotNullFn, vec![arg!(any)], Some(arg!(any)));
465
466impl Function for NotNullFn {
467 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
468 self.signature.validate(args, ctx)?;
469 for arg in args {
470 if !arg.is_null() {
471 return Ok(arg.clone());
472 }
473 }
474 Ok(Value::Null)
475 }
476}
477
478defn!(ReverseFn, vec![arg!(array | string)], None);
479
480impl Function for ReverseFn {
481 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
482 self.signature.validate(args, ctx)?;
483 match &args[0] {
484 Value::Array(arr) => {
485 let mut reversed = arr.clone();
486 reversed.reverse();
487 Ok(Value::Array(reversed))
488 }
489 Value::String(s) => {
490 let reversed: String = s.chars().rev().collect();
491 Ok(Value::String(reversed))
492 }
493 _ => unreachable!(),
494 }
495 }
496}
497
498defn!(SortFn, vec![arg!(array_string | array_number)], None);
499
500impl Function for SortFn {
501 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
502 self.signature.validate(args, ctx)?;
503 let mut values = args[0].as_array().unwrap().clone();
504 values.sort_by(compare_values);
505 Ok(Value::Array(values))
506 }
507}
508
509defn!(SortByFn, vec![arg!(array), arg!(expref)], None);
510
511impl Function for SortByFn {
512 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
513 self.signature.validate(args, ctx)?;
514 let vals = args[0].as_array().unwrap();
515 if vals.is_empty() {
516 return Ok(Value::Array(vec![]));
517 }
518 let ast = get_expref_ast(&args[1], ctx)
519 .ok_or_else(|| {
520 JmespathError::new("", 0, ErrorReason::Parse("Expected expref".to_owned()))
521 })?
522 .clone();
523
524 let first_value = interpret(&vals[0], &ast, ctx)?;
526 let first_type = first_value.jmespath_type();
527 if first_type != JmespathType::String && first_type != JmespathType::Number {
528 let reason = ErrorReason::Runtime(RuntimeError::InvalidReturnType {
529 expected: "expression->string|expression->number".to_owned(),
530 actual: first_type.to_string(),
531 position: 1,
532 invocation: 1,
533 });
534 return Err(JmespathError::from_ctx(ctx, reason));
535 }
536
537 let mut mapped: Vec<(Value, Value)> = vec![(vals[0].clone(), first_value)];
538 for (invocation, v) in vals.iter().enumerate().skip(1) {
539 let mapped_value = interpret(v, &ast, ctx)?;
540 if mapped_value.jmespath_type() != first_type {
541 return Err(JmespathError::from_ctx(
542 ctx,
543 ErrorReason::Runtime(RuntimeError::InvalidReturnType {
544 expected: format!("expression->{first_type}"),
545 actual: mapped_value.jmespath_type().to_string(),
546 position: 1,
547 invocation,
548 }),
549 ));
550 }
551 mapped.push((v.clone(), mapped_value));
552 }
553 mapped.sort_by(|a, b| compare_values(&a.1, &b.1));
554 let result = mapped.into_iter().map(|(orig, _)| orig).collect();
555 Ok(Value::Array(result))
556 }
557}
558
559defn!(StartsWithFn, vec![arg!(string), arg!(string)], None);
560
561impl Function for StartsWithFn {
562 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
563 self.signature.validate(args, ctx)?;
564 let subject = args[0].as_str().unwrap();
565 let search = args[1].as_str().unwrap();
566 Ok(Value::Bool(subject.starts_with(search)))
567 }
568}
569
570defn!(SumFn, vec![arg!(array_number)], None);
571
572impl Function for SumFn {
573 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
574 self.signature.validate(args, ctx)?;
575 let sum: f64 = args[0]
576 .as_array()
577 .unwrap()
578 .iter()
579 .map(|v| v.as_f64().unwrap_or(0.0))
580 .sum();
581 Ok(number_value(sum))
582 }
583}
584
585defn!(ToArrayFn, vec![arg!(any)], None);
586
587impl Function for ToArrayFn {
588 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
589 self.signature.validate(args, ctx)?;
590 match &args[0] {
591 Value::Array(_) => Ok(args[0].clone()),
592 _ => Ok(Value::Array(vec![args[0].clone()])),
593 }
594 }
595}
596
597defn!(ToNumberFn, vec![arg!(any)], None);
598
599impl Function for ToNumberFn {
600 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
601 self.signature.validate(args, ctx)?;
602 match &args[0] {
603 Value::Number(_) => Ok(args[0].clone()),
604 Value::String(s) => match serde_json::from_str::<Value>(s) {
605 Ok(Value::Number(n)) => Ok(Value::Number(n)),
606 _ => Ok(Value::Null),
607 },
608 _ => Ok(Value::Null),
609 }
610 }
611}
612
613defn!(
614 ToStringFn,
615 vec![arg!(object | array | bool | number | string | null)],
616 None
617);
618
619impl Function for ToStringFn {
620 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
621 self.signature.validate(args, ctx)?;
622 match &args[0] {
623 Value::String(_) => Ok(args[0].clone()),
624 other => Ok(Value::String(other.to_string())),
625 }
626 }
627}
628
629defn!(TypeFn, vec![arg!(any)], None);
630
631impl Function for TypeFn {
632 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
633 self.signature.validate(args, ctx)?;
634 Ok(Value::String(args[0].jmespath_type().to_string()))
635 }
636}
637
638defn!(ValuesFn, vec![arg!(object)], None);
639
640impl Function for ValuesFn {
641 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
642 self.signature.validate(args, ctx)?;
643 let map = args[0].as_object().unwrap();
644 Ok(Value::Array(map.values().cloned().collect()))
645 }
646}
647
648fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
652 match (a, b) {
653 (Value::Number(a), Value::Number(b)) => {
654 let af = a.as_f64().unwrap_or(0.0);
655 let bf = b.as_f64().unwrap_or(0.0);
656 af.partial_cmp(&bf).unwrap_or(std::cmp::Ordering::Equal)
657 }
658 (Value::String(a), Value::String(b)) => a.cmp(b),
659 _ => std::cmp::Ordering::Equal,
660 }
661}
662
663fn max_value(a: Value, b: Value) -> Value {
664 if compare_values(&a, &b).is_ge() { a } else { b }
665}
666
667fn min_value(a: Value, b: Value) -> Value {
668 if compare_values(&a, &b).is_le() { a } else { b }
669}
670
671fn min_max_by(
673 ctx: &mut Context<'_>,
674 array_arg: &Value,
675 expref_arg: &Value,
676 is_better: fn(&Value, &Value) -> bool,
677) -> SearchResult {
678 let vals = array_arg.as_array().ok_or_else(|| {
679 JmespathError::new("", 0, ErrorReason::Parse("Expected array".to_owned()))
680 })?;
681 if vals.is_empty() {
682 return Ok(Value::Null);
683 }
684 let ast = get_expref_ast(expref_arg, ctx)
685 .ok_or_else(|| JmespathError::new("", 0, ErrorReason::Parse("Expected expref".to_owned())))?
686 .clone();
687
688 let initial = interpret(&vals[0], &ast, ctx)?;
689 let entered_type = initial.jmespath_type();
690 if entered_type != JmespathType::String && entered_type != JmespathType::Number {
691 return Err(JmespathError::from_ctx(
692 ctx,
693 ErrorReason::Runtime(RuntimeError::InvalidReturnType {
694 expected: "expression->number|expression->string".to_owned(),
695 actual: entered_type.to_string(),
696 position: 1,
697 invocation: 1,
698 }),
699 ));
700 }
701
702 let mut candidate = (vals[0].clone(), initial);
703 for (invocation, v) in vals.iter().enumerate().skip(1) {
704 let mapped = interpret(v, &ast, ctx)?;
705 if mapped.jmespath_type() != entered_type {
706 return Err(JmespathError::from_ctx(
707 ctx,
708 ErrorReason::Runtime(RuntimeError::InvalidReturnType {
709 expected: format!("expression->{entered_type}"),
710 actual: mapped.jmespath_type().to_string(),
711 position: 1,
712 invocation,
713 }),
714 ));
715 }
716 if is_better(&mapped, &candidate.1) {
717 candidate = (v.clone(), mapped);
718 }
719 }
720 Ok(candidate.0)
721}