jexl_eval/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5//! A JEXL evaluator written in Rust
6//! This crate depends on a JEXL parser crate that handles all the parsing
7//! and is a part of the same workspace.
8//! JEXL is an expression language used by Mozilla, you can find more information here: https://github.com/mozilla/mozjexl
9//!
10//! # How to use
11//! The access point for this crate is the `eval` functions of the Evaluator Struct
12//! You can use the `eval` function directly to evaluate standalone statements
13//!
14//! For example:
15//! ```rust
16//! use jexl_eval::Evaluator;
17//! use serde_json::json as value;
18//! let evaluator = Evaluator::new();
19//! assert_eq!(evaluator.eval("'Hello ' + 'World'").unwrap(), value!("Hello World"));
20//! ```
21//!
22//! You can also run the statements against a context using the `eval_in_context` function
23//! The context can be any type that implements the `serde::Serializable` trait
24//! and the function will return errors if the statement doesn't match the context
25//!
26//! For example:
27//! ```rust
28//! use jexl_eval::Evaluator;
29//! use serde_json::json as value;
30//! let context = value!({"a": {"b": 2.0}});
31//! let evaluator = Evaluator::new();
32//! assert_eq!(evaluator.eval_in_context("a.b", context).unwrap(), value!(2.0));
33//! ```
34//!
35
36use jexl_parser::{
37    ast::{Expression, OpCode},
38    Parser,
39};
40use serde_json::{json as value, Value};
41
42pub mod error;
43use error::*;
44use std::collections::HashMap;
45
46const EPSILON: f64 = 0.000001f64;
47
48trait Truthy {
49    fn is_truthy(&self) -> bool;
50}
51
52impl Truthy for Value {
53    fn is_truthy(&self) -> bool {
54        match self {
55            Value::Bool(b) => *b,
56            Value::Null => false,
57            Value::Number(f) => f.as_f64().unwrap() != 0.0,
58            Value::String(s) => !s.is_empty(),
59            // It would be better if these depended on the contents of the
60            // object (empty array/object is falsey, non-empty is truthy, like
61            // in Python) but this matches JS semantics. Is it worth changing?
62            Value::Array(_) => true,
63            Value::Object(_) => true,
64        }
65    }
66}
67
68impl<'b> Truthy for Result<'b, Value> {
69    fn is_truthy(&self) -> bool {
70        match self {
71            Ok(v) => v.is_truthy(),
72            _ => false,
73        }
74    }
75}
76
77type Context = Value;
78
79/// TransformFn represents an arbitrary transform function
80/// Transform functions take an arbitrary number of `serde_json::Value`to represent their arguments
81/// and return a `serde_json::Value`.
82/// the transform function itself is responsible for checking if the format and number of
83/// the arguments is correct
84///
85/// Returns a Result with an `anyhow::Error`. This allows consumers to return their own custom errors
86/// in the closure, and use `.into` to convert it into an `anyhow::Error`. The error message will be perserved
87pub type TransformFn<'a> = Box<dyn Fn(&[Value]) -> Result<Value, anyhow::Error> + Send + Sync + 'a>;
88
89#[derive(Default)]
90pub struct Evaluator<'a> {
91    transforms: HashMap<String, TransformFn<'a>>,
92}
93
94impl<'a> Evaluator<'a> {
95    pub fn new() -> Self {
96        Self::default()
97    }
98
99    /// Adds a custom transform function
100    /// This is meant as a way to allow consumers to add their own custom functionality
101    /// to the expression language.
102    /// Note that the name added here has to match with
103    /// the name that the transform will have when it's a part of the expression statement
104    ///
105    /// # Arguments:
106    /// - `name`: The name of the transfrom
107    /// - `transform`: The actual function. A closure the implements Fn(&[serde_json::Value]) -> Result<Value, anyhow::Error>
108    ///
109    /// # Example:
110    ///
111    /// ```rust
112    /// use jexl_eval::Evaluator;
113    /// use serde_json::{json as value, Value};
114    ///
115    /// let mut evaluator = Evaluator::new().with_transform("lower", |v: &[Value]| {
116    ///    let s = v
117    ///            .first()
118    ///            .expect("Should have 1 argument!")
119    ///            .as_str()
120    ///            .expect("Should be a string!");
121    ///       Ok(value!(s.to_lowercase()))
122    ///  });
123    ///
124    /// assert_eq!(evaluator.eval("'JOHN DOe'|lower").unwrap(), value!("john doe"))
125    /// ```
126    pub fn with_transform<F>(mut self, name: &str, transform: F) -> Self
127    where
128        F: Fn(&[Value]) -> Result<Value, anyhow::Error> + Send + Sync + 'a,
129    {
130        self.transforms
131            .insert(name.to_string(), Box::new(transform));
132        self
133    }
134
135    pub fn eval<'b>(&self, input: &'b str) -> Result<'b, Value> {
136        let context = value!({});
137        self.eval_in_context(input, &context)
138    }
139
140    pub fn eval_in_context<'b, T: serde::Serialize>(
141        &self,
142        input: &'b str,
143        context: T,
144    ) -> Result<'b, Value> {
145        let tree = Parser::parse(input)?;
146        let context = serde_json::to_value(context)?;
147        if !context.is_object() {
148            return Err(EvaluationError::InvalidContext);
149        }
150        self.eval_ast(tree, &context)
151    }
152
153    fn eval_ast<'b>(&self, ast: Expression, context: &Context) -> Result<'b, Value> {
154        match ast {
155            Expression::Number(n) => Ok(value!(n)),
156            Expression::Boolean(b) => Ok(value!(b)),
157            Expression::String(s) => Ok(value!(s)),
158            Expression::Array(xs) => xs.into_iter().map(|x| self.eval_ast(*x, context)).collect(),
159            Expression::Null => Ok(Value::Null),
160
161            Expression::Object(items) => {
162                let mut map = serde_json::Map::with_capacity(items.len());
163                for (key, expr) in items.into_iter() {
164                    if map.contains_key(&key) {
165                        return Err(EvaluationError::DuplicateObjectKey(key));
166                    }
167                    let value = self.eval_ast(*expr, context)?;
168                    map.insert(key, value);
169                }
170                Ok(Value::Object(map))
171            }
172
173            Expression::Identifier(inner) => match context.get(&inner) {
174                Some(v) => Ok(v.clone()),
175                _ => Err(EvaluationError::UndefinedIdentifier(inner.clone())),
176            },
177
178            Expression::DotOperation { subject, ident } => {
179                let subject = self.eval_ast(*subject, context)?;
180                Ok(subject.get(&ident).unwrap_or(&value!(null)).clone())
181            }
182
183            Expression::IndexOperation { subject, index } => {
184                let subject = self.eval_ast(*subject, context)?;
185                if let Expression::Filter { ident, op, right } = *index {
186                    let subject_arr = subject.as_array().ok_or(EvaluationError::InvalidFilter)?;
187                    let right = self.eval_ast(*right, context)?;
188                    let filtered = subject_arr
189                        .iter()
190                        .filter(|e| {
191                            let left = e.get(&ident).unwrap_or(&value!(null));
192                            // returns false if any members fail the op, could happen if array members are missing the identifier
193                            Self::apply_op(op, left.clone(), right.clone())
194                                .unwrap_or(value!(false))
195                                .is_truthy()
196                        })
197                        .collect::<Vec<_>>();
198                    return Ok(value!(filtered));
199                }
200
201                let index = self.eval_ast(*index, context)?;
202                match index {
203                    Value::String(inner) => {
204                        Ok(subject.get(&inner).unwrap_or(&value!(null)).clone())
205                    }
206                    Value::Number(inner) => Ok(subject
207                        .get(inner.as_f64().unwrap().floor() as usize)
208                        .unwrap_or(&value!(null))
209                        .clone()),
210                    _ => Err(EvaluationError::InvalidIndexType),
211                }
212            }
213
214            Expression::BinaryOperation {
215                left,
216                right,
217                operation,
218            } => self.eval_op(operation, left, right, context),
219            Expression::Transform {
220                name,
221                subject,
222                args,
223            } => {
224                let subject = self.eval_ast(*subject, context)?;
225                let mut args_arr = Vec::new();
226                args_arr.push(subject);
227                if let Some(args) = args {
228                    for arg in args {
229                        args_arr.push(self.eval_ast(*arg, context)?);
230                    }
231                }
232                let f = self
233                    .transforms
234                    .get(&name)
235                    .ok_or(EvaluationError::UnknownTransform(name))?;
236                f(&args_arr).map_err(|e| e.into())
237            }
238
239            Expression::Conditional {
240                left,
241                truthy,
242                falsy,
243            } => {
244                if self.eval_ast(*left, context).is_truthy() {
245                    self.eval_ast(*truthy, context)
246                } else {
247                    self.eval_ast(*falsy, context)
248                }
249            }
250
251            Expression::Filter {
252                ident: _,
253                op: _,
254                right: _,
255            } => {
256                // Filters shouldn't be evaluated individually
257                // instead, they are evaluated as a part of an IndexOperation
258                return Err(EvaluationError::InvalidFilter);
259            }
260        }
261    }
262
263    fn eval_op<'b>(
264        &self,
265        operation: OpCode,
266        left: Box<Expression>,
267        right: Box<Expression>,
268        context: &Context,
269    ) -> Result<'b, Value> {
270        let left = self.eval_ast(*left, context);
271
272        // We want to delay evaluating the right hand side in the cases of AND and OR.
273        let eval_right = || self.eval_ast(*right, context);
274        Ok(match operation {
275            OpCode::Or => {
276                if left.is_truthy() {
277                    left?
278                } else {
279                    eval_right()?
280                }
281            }
282            OpCode::And => {
283                if left.is_truthy() {
284                    eval_right()?
285                } else {
286                    left?
287                }
288            }
289            _ => Self::apply_op(operation, left?, eval_right()?)?,
290        })
291    }
292
293    fn apply_op<'b>(operation: OpCode, left: Value, right: Value) -> Result<'b, Value> {
294        match (operation, left, right) {
295            (OpCode::NotEqual, a, b) => {
296                // Implement NotEquals as the inverse of Equals.
297                let value = Self::apply_op(OpCode::Equal, a, b)?;
298                let equality = value
299                    .as_bool()
300                    .unwrap_or_else(|| unreachable!("Equality always returns a bool"));
301                Ok(value!(!equality))
302            }
303
304            (OpCode::And, a, b) => Ok(if a.is_truthy() { b } else { a }),
305            (OpCode::Or, a, b) => Ok(if a.is_truthy() { a } else { b }),
306
307            (op, Value::Number(a), Value::Number(b)) => {
308                let left = a.as_f64().unwrap();
309                let right = b.as_f64().unwrap();
310                Ok(match op {
311                    OpCode::Add => value!(left + right),
312                    OpCode::Subtract => value!(left - right),
313                    OpCode::Multiply => value!(left * right),
314                    OpCode::Divide => value!(left / right),
315                    OpCode::FloorDivide => value!((left / right).floor()),
316                    OpCode::Modulus => value!(left % right),
317                    OpCode::Exponent => value!(left.powf(right)),
318                    OpCode::Less => value!(left < right),
319                    OpCode::Greater => value!(left > right),
320                    OpCode::LessEqual => value!(left <= right),
321                    OpCode::GreaterEqual => value!(left >= right),
322                    OpCode::Equal => value!((left - right).abs() < EPSILON),
323                    OpCode::NotEqual => value!((left - right).abs() >= EPSILON),
324                    OpCode::In => value!(false),
325                    OpCode::And | OpCode::Or => {
326                        unreachable!("Covered by previous case in parent match")
327                    }
328                })
329            }
330
331            (op, Value::String(a), Value::String(b)) => match op {
332                OpCode::Equal => Ok(value!(a == b)),
333
334                OpCode::Add => Ok(value!(format!("{}{}", a, b))),
335                OpCode::In => Ok(value!(b.contains(&a))),
336
337                OpCode::Less => Ok(value!(a < b)),
338                OpCode::Greater => Ok(value!(a > b)),
339                OpCode::LessEqual => Ok(value!(a <= b)),
340                OpCode::GreaterEqual => Ok(value!(a >= b)),
341
342                _ => Err(EvaluationError::InvalidBinaryOp {
343                    operation,
344                    left: value!(a),
345                    right: value!(b),
346                }),
347            },
348
349            (OpCode::In, left, Value::Array(v)) => Ok(value!(v.contains(&left))),
350            (OpCode::In, Value::String(left), Value::Object(v)) => {
351                Ok(value!(v.contains_key(&left)))
352            }
353
354            (OpCode::Equal, a, b) => match (a, b) {
355                // Number == Number is handled above
356                // String == String is handled above
357                (Value::Bool(a), Value::Bool(b)) => Ok(value!(a == b)),
358                (Value::Null, Value::Null) => Ok(value!(true)),
359                (Value::Array(a), Value::Array(b)) => Ok(value!(a == b)),
360                (Value::Object(a), Value::Object(b)) => Ok(value!(a == b)),
361                // If the types don't match, it's always false
362                _ => Ok(value!(false)),
363            },
364
365            (operation, left, right) => Err(EvaluationError::InvalidBinaryOp {
366                operation,
367                left,
368                right,
369            }),
370        }
371    }
372}
373
374#[cfg(test)]
375mod tests {
376    use super::*;
377    use serde_json::json as value;
378
379    #[test]
380    fn test_literal() {
381        assert_eq!(Evaluator::new().eval("1").unwrap(), value!(1.0));
382    }
383
384    #[test]
385    fn test_binary_expression_addition() {
386        assert_eq!(Evaluator::new().eval("1 + 2").unwrap(), value!(3.0));
387    }
388
389    #[test]
390    fn test_binary_expression_multiplication() {
391        assert_eq!(Evaluator::new().eval("2 * 3").unwrap(), value!(6.0));
392    }
393
394    #[test]
395    fn test_precedence() {
396        assert_eq!(Evaluator::new().eval("2 + 3 * 4").unwrap(), value!(14.0));
397    }
398
399    #[test]
400    fn test_parenthesis() {
401        assert_eq!(Evaluator::new().eval("(2 + 3) * 4").unwrap(), value!(20.0));
402    }
403
404    #[test]
405    fn test_string_concat() {
406        assert_eq!(
407            Evaluator::new().eval("'Hello ' + 'World'").unwrap(),
408            value!("Hello World")
409        );
410    }
411
412    #[test]
413    fn test_true_comparison() {
414        assert_eq!(Evaluator::new().eval("2 > 1").unwrap(), value!(true));
415    }
416
417    #[test]
418    fn test_false_comparison() {
419        assert_eq!(Evaluator::new().eval("2 <= 1").unwrap(), value!(false));
420    }
421
422    #[test]
423    fn test_boolean_logic() {
424        assert_eq!(
425            Evaluator::new()
426                .eval("'foo' && 6 >= 6 && 0 + 1 && true")
427                .unwrap(),
428            value!(true)
429        );
430    }
431
432    #[test]
433    fn test_identifier() {
434        let context = value!({"a": 1.0});
435        assert_eq!(
436            Evaluator::new().eval_in_context("a", context).unwrap(),
437            value!(1.0)
438        );
439    }
440
441    #[test]
442    fn test_identifier_chain() {
443        let context = value!({"a": {"b": 2.0}});
444        assert_eq!(
445            Evaluator::new().eval_in_context("a.b", context).unwrap(),
446            value!(2.0)
447        );
448    }
449
450    #[test]
451    fn test_context_filter_arrays() {
452        let context = value!({
453            "foo": {
454                "bar": [
455                    {"tek": "hello"},
456                    {"tek": "baz"},
457                    {"tok": "baz"},
458                ]
459            }
460        });
461        assert_eq!(
462            Evaluator::new()
463                .eval_in_context("foo.bar[.tek == 'baz']", &context)
464                .unwrap(),
465            value!([{"tek": "baz"}])
466        );
467    }
468
469    #[test]
470    fn test_context_array_index() {
471        let context = value!({
472            "foo": {
473                "bar": [
474                    {"tek": "hello"},
475                    {"tek": "baz"},
476                    {"tok": "baz"},
477                ]
478            }
479        });
480        assert_eq!(
481            Evaluator::new()
482                .eval_in_context("foo.bar[1].tek", context)
483                .unwrap(),
484            value!("baz")
485        );
486    }
487
488    #[test]
489    fn test_object_expression_properties() {
490        let context = value!({"foo": {"baz": {"bar": "tek"}}});
491        assert_eq!(
492            Evaluator::new()
493                .eval_in_context("foo['ba' + 'z'].bar", &context)
494                .unwrap(),
495            value!("tek")
496        );
497    }
498
499    #[test]
500    fn test_divfloor() {
501        assert_eq!(Evaluator::new().eval("7 // 2").unwrap(), value!(3.0));
502    }
503
504    #[test]
505    fn test_empty_object_literal() {
506        assert_eq!(Evaluator::new().eval("{}").unwrap(), value!({}));
507    }
508
509    #[test]
510    fn test_object_literal_strings() {
511        assert_eq!(
512            Evaluator::new().eval("{'foo': {'bar': 'tek'}}").unwrap(),
513            value!({"foo": {"bar": "tek"}})
514        );
515    }
516
517    #[test]
518    fn test_object_literal_identifiers() {
519        assert_eq!(
520            Evaluator::new().eval("{foo: {bar: 'tek'}}").unwrap(),
521            value!({"foo": {"bar": "tek"}})
522        );
523    }
524
525    #[test]
526    fn test_object_literal_properties() {
527        assert_eq!(
528            Evaluator::new().eval("{foo: 'bar'}.foo").unwrap(),
529            value!("bar")
530        );
531    }
532
533    #[test]
534    fn test_array_literal() {
535        assert_eq!(
536            Evaluator::new().eval("['foo', 1+2]").unwrap(),
537            value!(["foo", 3.0])
538        );
539    }
540
541    #[test]
542    fn test_array_literal_indexing() {
543        assert_eq!(Evaluator::new().eval("[1, 2, 3][1]").unwrap(), value!(2.0));
544    }
545
546    #[test]
547    fn test_in_operator_string() {
548        assert_eq!(
549            Evaluator::new().eval("'bar' in 'foobartek'").unwrap(),
550            value!(true)
551        );
552        assert_eq!(
553            Evaluator::new().eval("'baz' in 'foobartek'").unwrap(),
554            value!(false)
555        );
556    }
557
558    #[test]
559    fn test_in_operator_array() {
560        assert_eq!(
561            Evaluator::new()
562                .eval("'bar' in ['foo', 'bar', 'tek']")
563                .unwrap(),
564            value!(true)
565        );
566        assert_eq!(
567            Evaluator::new()
568                .eval("'baz' in ['foo', 'bar', 'tek']")
569                .unwrap(),
570            value!(false)
571        );
572    }
573
574    #[test]
575    fn test_in_operator_object() {
576        assert_eq!(
577            Evaluator::new()
578                .eval("'bar' in {foo: 1, bar: 2, tek: 3}")
579                .unwrap(),
580            value!(true)
581        );
582        assert_eq!(
583            Evaluator::new()
584                .eval("'baz' in {foo: 1, bar: 2, tek: 3}")
585                .unwrap(),
586            value!(false)
587        );
588    }
589
590    #[test]
591    fn test_conditional_expression() {
592        assert_eq!(
593            Evaluator::new().eval("'foo' ? 1 : 2").unwrap(),
594            value!(1f64)
595        );
596        assert_eq!(Evaluator::new().eval("'' ? 1 : 2").unwrap(), value!(2f64));
597    }
598
599    #[test]
600    fn test_arbitrary_whitespace() {
601        assert_eq!(
602            Evaluator::new().eval("(\t2\n+\n3) *\n4\n\r\n").unwrap(),
603            value!(20.0)
604        );
605    }
606
607    #[test]
608    fn test_non_integer() {
609        assert_eq!(Evaluator::new().eval("1.5 * 3.0").unwrap(), value!(4.5));
610    }
611
612    #[test]
613    fn test_string_literal() {
614        assert_eq!(
615            Evaluator::new().eval("'hello world'").unwrap(),
616            value!("hello world")
617        );
618        assert_eq!(
619            Evaluator::new().eval("\"hello world\"").unwrap(),
620            value!("hello world")
621        );
622    }
623
624    #[test]
625    fn test_string_escapes() {
626        assert_eq!(Evaluator::new().eval("'a\\'b'").unwrap(), value!("a'b"));
627        assert_eq!(Evaluator::new().eval("\"a\\\"b\"").unwrap(), value!("a\"b"));
628    }
629
630    #[test]
631    // Test a very simple transform that applies to_lowercase to a string
632    fn test_simple_transform() {
633        let evaluator = Evaluator::new().with_transform("lower", |v: &[Value]| {
634            let s = v
635                .get(0)
636                .expect("There should be one argument!")
637                .as_str()
638                .expect("Should be a string!");
639            Ok(value!(s.to_lowercase()))
640        });
641        assert_eq!(evaluator.eval("'T_T'|lower").unwrap(), value!("t_t"));
642    }
643
644    #[test]
645    // Test returning an UnknownTransform error if a transform is unknown
646    fn test_missing_transform() {
647        let err = Evaluator::new().eval("'hello'|world").unwrap_err();
648        if let EvaluationError::UnknownTransform(transform) = err {
649            assert_eq!(transform, "world")
650        } else {
651            panic!("Should have thrown an unknown transform error")
652        }
653    }
654
655    #[test]
656    // Test returning an UndefinedIdentifier error if an identifier is unknown
657    fn test_undefined_identifier() {
658        let err = Evaluator::new().eval("not_defined").unwrap_err();
659        if let EvaluationError::UndefinedIdentifier(id) = err {
660            assert_eq!(id, "not_defined")
661        } else {
662            panic!("Should have thrown an undefined identifier error")
663        }
664    }
665
666    #[test]
667    // Test returning an UndefinedIdentifier error if an identifier is unknown
668    fn test_undefined_identifier_truthy_ops() {
669        let err = Evaluator::new().eval("not_defined").unwrap_err();
670        if let EvaluationError::UndefinedIdentifier(id) = err {
671            assert_eq!(id, "not_defined")
672        } else {
673            panic!("Should have thrown an undefined identifier error")
674        }
675
676        let evaluator = Evaluator::new();
677        let context = value!({
678            "NULL": null,
679            "DEFINED": "string",
680        });
681
682        let test = |expr: &str, is_ok: bool, exp: Value| {
683            let obs = evaluator.eval_in_context(&expr, context.clone());
684            if !is_ok {
685                assert!(obs.is_err());
686                assert!(matches!(
687                    obs.unwrap_err(),
688                    EvaluationError::UndefinedIdentifier(_)
689                ));
690            } else {
691                assert_eq!(obs.unwrap(), exp,);
692            }
693        };
694
695        test("UNDEFINED", false, value!(null));
696        test("UNDEFINED == 'string'", false, value!(null));
697        test("'string' == UNDEFINED", false, value!(null));
698
699        test("UNDEFINED ? 'WRONG' : 'RIGHT'", true, value!("RIGHT"));
700        test("DEFINED ? UNDEFINED : 'WRONG'", false, value!(null));
701
702        test("UNDEFINED || 'RIGHT'", true, value!("RIGHT"));
703        test("'RIGHT' || UNDEFINED", true, value!("RIGHT"));
704
705        test("'WRONG' && UNDEFINED", false, value!(null));
706        test("UNDEFINED && 'WRONG'", false, value!(null));
707
708        test("UNDEFINED && 'WRONG'", false, value!(null));
709
710        test(
711            "(UNDEFINED && UNDEFINED == 'string') || (DEFINED && DEFINED == 'string')",
712            true,
713            value!(true),
714        );
715    }
716
717    #[test]
718    fn test_add_multiple_transforms() {
719        let evaluator = Evaluator::new()
720            .with_transform("sqrt", |v: &[Value]| {
721                let num = v
722                    .first()
723                    .expect("There should be one argument!")
724                    .as_f64()
725                    .expect("Should be a valid number!");
726                Ok(value!(num.sqrt() as u64))
727            })
728            .with_transform("square", |v: &[Value]| {
729                let num = v
730                    .first()
731                    .expect("There should be one argument!")
732                    .as_f64()
733                    .expect("Should be a valid number!");
734                Ok(value!((num as u64).pow(2)))
735            });
736
737        assert_eq!(evaluator.eval("4|square").unwrap(), value!(16));
738        assert_eq!(evaluator.eval("4|sqrt").unwrap(), value!(2));
739        assert_eq!(evaluator.eval("4|square|sqrt").unwrap(), value!(4));
740    }
741
742    #[test]
743    fn test_transform_with_argument() {
744        let evaluator = Evaluator::new().with_transform("split", |args: &[Value]| {
745            let s = args
746                .first()
747                .expect("Should be a first argument!")
748                .as_str()
749                .expect("Should be a string!");
750            let c = args
751                .get(1)
752                .expect("There should be a second argument!")
753                .as_str()
754                .expect("Should be a string");
755            let res: Vec<&str> = s.split_terminator(c).collect();
756            Ok(value!(res))
757        });
758
759        assert_eq!(
760            evaluator.eval("'John Doe'|split(' ')").unwrap(),
761            value!(vec!["John", "Doe"])
762        );
763    }
764
765    #[derive(Debug, thiserror::Error)]
766    enum CustomError {
767        #[error("Invalid argument in transform!")]
768        InvalidArgument,
769    }
770
771    #[test]
772    fn test_custom_error_message() {
773        let evaluator = Evaluator::new().with_transform("error", |_: &[Value]| {
774            Err(CustomError::InvalidArgument.into())
775        });
776        let res = evaluator.eval("1234|error");
777        assert!(res.is_err());
778        if let EvaluationError::CustomError(e) = res.unwrap_err() {
779            assert_eq!(e.to_string(), "Invalid argument in transform!")
780        } else {
781            panic!("Should have returned a Custom error!")
782        }
783    }
784
785    #[test]
786    fn test_filter_collections_many_returned() {
787        let evaluator = Evaluator::new();
788        let context = value!({
789            "foo": [
790                {"bobo": 50, "fofo": 100},
791                {"bobo": 60, "baz": 90},
792                {"bobo": 10, "bar": 83},
793                {"bobo": 20, "yam": 12},
794            ]
795        });
796        let exp = "foo[.bobo >= 50]";
797        assert_eq!(
798            evaluator.eval_in_context(exp, context).unwrap(),
799            value!([{"bobo": 50, "fofo": 100}, {"bobo": 60, "baz": 90}])
800        );
801    }
802
803    #[test]
804    fn test_binary_op_eq_ne() {
805        let evaluator = Evaluator::new();
806        let context = value!({
807            "NULL": null,
808            "STRING": "string",
809            "BOOLEAN": true,
810            "NUMBER": 42,
811            "OBJECT": { "x": 1, "y": 2 },
812            "ARRAY": [ "string" ]
813        });
814
815        let test = |l: &str, r: &str, exp: bool| {
816            let expr = format!("{} == {}", l, r);
817            assert_eq!(
818                evaluator.eval_in_context(&expr, context.clone()).unwrap(),
819                value!(exp)
820            );
821
822            let expr = format!("{} != {}", l, r);
823            assert_eq!(
824                evaluator.eval_in_context(&expr, context.clone()).unwrap(),
825                value!(!exp)
826            );
827        };
828
829        test("STRING", "'string'", true);
830        test("NUMBER", "42", true);
831        test("BOOLEAN", "true", true);
832        test("OBJECT", "OBJECT", true);
833        test("ARRAY", "[ 'string' ]", true);
834        test("NULL", "null", true);
835
836        test("OBJECT", "{ 'x': 1, 'y': 2 }", false);
837
838        test("STRING", "NULL", false);
839        test("NUMBER", "NULL", false);
840        test("BOOLEAN", "NULL", false);
841        // test("NULL", "NULL", false);
842        test("OBJECT", "NULL", false);
843        test("ARRAY", "NULL", false);
844
845        // test("STRING", "STRING", false);
846        test("NUMBER", "STRING", false);
847        test("BOOLEAN", "STRING", false);
848        test("NULL", "STRING", false);
849        test("OBJECT", "STRING", false);
850        test("ARRAY", "STRING", false);
851
852        test("STRING", "NUMBER", false);
853        // test("NUMBER", "NUMBER", false);
854        test("BOOLEAN", "NUMBER", false);
855        test("NULL", "NUMBER", false);
856        test("OBJECT", "NUMBER", false);
857        test("ARRAY", "NUMBER", false);
858
859        test("STRING", "BOOLEAN", false);
860        test("NUMBER", "BOOLEAN", false);
861        // test("BOOLEAN", "BOOLEAN", false);
862        test("NULL", "BOOLEAN", false);
863        test("OBJECT", "BOOLEAN", false);
864        test("ARRAY", "BOOLEAN", false);
865
866        test("STRING", "OBJECT", false);
867        test("NUMBER", "OBJECT", false);
868        test("BOOLEAN", "OBJECT", false);
869        test("NULL", "OBJECT", false);
870        // test("OBJECT", "OBJECT", false);
871        test("ARRAY", "OBJECT", false);
872
873        test("STRING", "ARRAY", false);
874        test("NUMBER", "ARRAY", false);
875        test("BOOLEAN", "ARRAY", false);
876        test("NULL", "ARRAY", false);
877        test("OBJECT", "ARRAY", false);
878        // test("ARRAY", "ARRAY", false);
879    }
880
881    #[test]
882    fn test_binary_op_string_gt_lt_gte_lte() {
883        let evaluator = Evaluator::new();
884        let context = value!({
885            "A": "A string",
886            "B": "B string",
887        });
888
889        let test = |l: &str, r: &str, is_gt: bool| {
890            let expr = format!("{} > {}", l, r);
891            assert_eq!(
892                evaluator.eval_in_context(&expr, context.clone()).unwrap(),
893                value!(is_gt)
894            );
895
896            let expr = format!("{} <= {}", l, r);
897            assert_eq!(
898                evaluator.eval_in_context(&expr, context.clone()).unwrap(),
899                value!(!is_gt)
900            );
901
902            // we test equality in another test
903            let expr = format!("{} == {}", l, r);
904            let is_eq = evaluator
905                .eval_in_context(&expr, context.clone())
906                .unwrap()
907                .as_bool()
908                .unwrap();
909
910            if is_eq {
911                let expr = format!("{} >= {}", l, r);
912                assert_eq!(
913                    evaluator.eval_in_context(&expr, context.clone()).unwrap(),
914                    value!(true)
915                );
916            } else {
917                let expr = format!("{} < {}", l, r);
918                assert_eq!(
919                    evaluator.eval_in_context(&expr, context.clone()).unwrap(),
920                    value!(!is_gt)
921                );
922            }
923        };
924
925        test("A", "B", false);
926        test("B", "A", true);
927        test("A", "A", false);
928    }
929
930    #[test]
931    fn test_lazy_eval_binary_op_and_or() {
932        let evaluator = Evaluator::new();
933        // error is a missing transform
934        let res = evaluator.eval("42 || 0|error");
935        assert!(res.is_ok());
936        assert_eq!(res.unwrap(), value!(42.0));
937
938        let res = evaluator.eval("false || 0|error");
939        assert!(res.is_err());
940
941        let res = evaluator.eval("42 && 0|error");
942        assert!(res.is_err());
943
944        let res = evaluator.eval("false && 0|error");
945        assert!(res.is_ok());
946        assert_eq!(res.unwrap(), value!(false));
947    }
948
949    #[test]
950    fn test_lazy_eval_trinary_op() {
951        let evaluator = Evaluator::new();
952        // error is a missing transform
953        let res = evaluator.eval("true ? 42 : 0|error");
954        assert!(res.is_ok());
955        assert_eq!(res.unwrap(), value!(42.0));
956
957        let res = evaluator.eval("true ? 0|error : 42");
958        assert!(res.is_err());
959
960        let res = evaluator.eval("true ? 0|error : 42");
961        assert!(res.is_err());
962
963        let res = evaluator.eval("false ? 0|error : 42");
964        assert!(res.is_ok());
965        assert_eq!(res.unwrap(), value!(42.0));
966    }
967}