Skip to main content

pdk_pel/runtime/
mod.rs

1// Copyright (c) 2026, Salesforce, Inc.,
2// All rights reserved.
3// For full license text, see the LICENSE.txt file
4
5mod coercion;
6mod eval;
7mod prelude;
8mod value_handler;
9
10mod document;
11pub mod value;
12
13use std::collections::HashMap;
14use thiserror::Error;
15
16use crate::{
17    expression::{Expression, Symbol},
18    runtime::value::Value,
19    Location, Reference,
20};
21
22pub use value_handler::ValueHandler;
23
24#[derive(Error, Debug)]
25#[error("{kind}")]
26/// A struct bundling the [`RuntimeErrorKind`] with the [`Location`] where the error originated from.
27pub struct RuntimeError {
28    location: Location,
29    kind: RuntimeErrorKind,
30}
31
32impl RuntimeError {
33    /// Create a new instance
34    pub fn new(location: Location, kind: RuntimeErrorKind) -> Self {
35        Self { location, kind }
36    }
37
38    /// Get the type of the error.
39    pub fn kind(&self) -> &RuntimeErrorKind {
40        &self.kind
41    }
42
43    /// Get the position from the source string where the error occurred.
44    pub fn location(&self) -> Location {
45        self.location
46    }
47}
48
49/// Represents the different kinds of errors that can occur during runtime evaluation.
50#[derive(Error, Debug, Clone, Eq, PartialEq)]
51pub enum RuntimeErrorKind {
52    /// Occurs when trying to resolve an undefined reference.
53    #[error("Unknown reference")]
54    UnknownReference,
55
56    /// Occurs when trying to use an undefined symbol.
57    #[error("Unknown symbol `{0}`")]
58    UnknownSymbol(String),
59
60    /// Occurs when an operation is performed on incompatible types.
61    #[error("Type mismatch")]
62    TypeMismatch,
63
64    /// Occurs when a function is called with fewer arguments than required.
65    #[error("Not enough arguments")]
66    NotEnoughArguments,
67
68    /// Occurs when a function is called with more arguments than it accepts.
69    #[error("Too many arguments")]
70    TooManyArguments,
71
72    /// Occurs when trying to select an unsupported property or element.
73    #[error("Unsupported selection")]
74    UnsupportedSelection,
75
76    /// Occurs when a random number generator is not available.
77    #[error("Unavailable random generator")]
78    UnavailableRandomGenerator,
79
80    /// Occurs when trying to get the size of a value that doesn't support it.
81    #[error("Unavailable size")]
82    UnavailableSize,
83
84    /// Occurs when trying to compare values of incompatible types.
85    #[error("Uncomparable types")]
86    UncomparableTypes,
87}
88
89/// The result of checking if a symbol is available.
90pub enum Binding {
91    /// The value was bound and is available for usage.
92    Available(Value),
93    /// There is information that the binding should be available in the context was not yet bound.
94    Pending,
95    /// There is no information regarding if the binding will be available.
96    Unknown,
97}
98
99/// The context in which an expression is evaluated.
100pub trait Context {
101    /// Check if the of a symbol is available.
102    fn resolve(&self, symbol: &Symbol) -> Binding;
103
104    /// Solve for the value of a symbol.
105    fn value_handler(&self, reference: Reference) -> Option<&dyn ValueHandler>;
106}
107
108type Prelude = HashMap<&'static str, Value>;
109
110impl Context for Prelude {
111    fn resolve(&self, symbol: &Symbol) -> Binding {
112        self.get(symbol.as_str())
113            .cloned()
114            .map(Binding::Available)
115            .unwrap_or(Binding::Unknown)
116    }
117
118    fn value_handler(&self, _reference: Reference) -> Option<&dyn ValueHandler> {
119        None
120    }
121}
122
123struct MergedContext<'a> {
124    root: &'a dyn Context,
125    current: &'a dyn Context,
126}
127
128struct EmptyContext;
129
130impl Context for EmptyContext {
131    fn resolve(&self, _: &Symbol) -> Binding {
132        Binding::Unknown
133    }
134
135    fn value_handler(&self, _reference: Reference) -> Option<&dyn ValueHandler> {
136        None
137    }
138}
139
140impl<'a> MergedContext<'a> {
141    fn new(root: &'a dyn Context, current: &'a dyn Context) -> Self {
142        Self { root, current }
143    }
144}
145
146impl Context for MergedContext<'_> {
147    fn resolve(&self, symbol: &Symbol) -> Binding {
148        match self.current.resolve(symbol) {
149            Binding::Unknown => self.root.resolve(symbol),
150            b => b,
151        }
152    }
153
154    fn value_handler(&self, reference: Reference) -> Option<&dyn ValueHandler> {
155        self.current
156            .value_handler(reference)
157            .or_else(|| self.root.value_handler(reference))
158    }
159}
160
161#[derive(Debug, PartialEq, Clone)]
162/// The value of passing an [`Expression`] through a [`Runtime`].
163pub enum Evaluation {
164    /// The evaluation was completed
165    Complete(Location, Value),
166    /// The expression was partially computed and will need to be re-evaluated with a more complete
167    /// [`Context`]
168    Partial(Expression),
169}
170
171impl Evaluation {
172    pub fn map<F: FnOnce(Value) -> Value>(self, op: F) -> Self {
173        match self {
174            Self::Complete(l, v) => Self::Complete(l, op(v)),
175            e => e,
176        }
177    }
178
179    pub fn map_unsafe<F: FnOnce(Value) -> Result<Value, RuntimeError>>(
180        self,
181        op: F,
182    ) -> Result<Self, RuntimeError> {
183        match self {
184            Self::Complete(l, v) => Ok(Self::Complete(l, op(v)?)),
185            e => Ok(e),
186        }
187    }
188
189    pub fn map_partial<F: FnOnce(Expression) -> Expression>(self, op: F) -> Self {
190        match self {
191            Self::Partial(e) => Self::Partial(op(e)),
192            e => e,
193        }
194    }
195
196    pub fn complete(self) -> Option<Value> {
197        match self {
198            Self::Complete(_, v) => Some(v),
199            _ => None,
200        }
201    }
202
203    pub fn partial(self) -> Option<Expression> {
204        match self {
205            Self::Partial(expression) => Some(expression),
206            _ => None,
207        }
208    }
209
210    pub fn is_complete(&self) -> bool {
211        matches!(self, Self::Complete(_, _))
212    }
213
214    pub fn into_expression(self) -> Expression {
215        match self {
216            Self::Complete(location, value) => Expression::new(location, value),
217            Self::Partial(expression) => expression,
218        }
219    }
220}
221
222/// Represents an entity that can be evaluated in a particular context.
223pub trait Eval {
224    /// Try to resolve in the given context.
225    fn eval(&self, context: &dyn Context) -> Result<Evaluation, RuntimeError>;
226}
227
228/// The environment for evaluating expressions.
229pub struct Runtime<P = Prelude> {
230    prelude: P,
231}
232
233impl Runtime {
234    pub fn new() -> Self {
235        Self::with_prelude(prelude::prelude())
236    }
237}
238
239impl Default for Runtime {
240    fn default() -> Self {
241        Self::new()
242    }
243}
244
245impl<P: Context> Runtime<P> {
246    #[doc(hidden)]
247    /// Create a new instance with the given prelude.
248    pub fn with_prelude(prelude: P) -> Self {
249        Self { prelude }
250    }
251}
252
253impl Runtime {
254    /// Evaluate the expression solving references from the given context.
255    pub fn eval_with_context(
256        &self,
257        e: &dyn Eval,
258        context: &dyn Context,
259    ) -> Result<Evaluation, RuntimeError> {
260        let context = MergedContext::new(&self.prelude, context);
261        e.eval(&context).map(|ev| {
262            ev.map(|v| {
263                v.to_value_handler(&context)
264                    .and_then(|vh| vh.detach())
265                    .unwrap_or_else(Value::null)
266            })
267        })
268    }
269
270    /// Evaluate the expression without any external references.
271    pub fn eval(&self, e: &dyn Eval) -> Result<Evaluation, RuntimeError> {
272        self.eval_with_context(e, &EmptyContext)
273    }
274}
275
276#[cfg(test)]
277mod tests {
278
279    use std::collections::HashMap;
280    use std::rc::Rc;
281
282    use super::value::{Object, QualifiedName, Value};
283    use crate::{
284        expression::Symbol,
285        parser::Parser,
286        runtime::{Binding, Context, RuntimeErrorKind, ValueHandler},
287        ContextId, Reference,
288    };
289
290    use super::{Prelude, Runtime, RuntimeError};
291    use crate::runtime::document::DocumentBuilder;
292    use test_case::test_case;
293
294    #[test]
295    fn inline_array() {
296        // DW: ["one", 2, false]
297        let json = r#"[":array", "0-17", [":str", "1-6", "one"], [":nbr", "8-9", "2"], [":bool", "11-16", "false"]]"#;
298
299        let expression = Parser::new().parse_str(json).unwrap();
300        let actual = Runtime::new()
301            .eval(&expression)
302            .unwrap()
303            .complete()
304            .unwrap();
305        let expected = &[
306            Value::string("one".to_string()),
307            Value::number(2.0),
308            Value::bool(false),
309        ];
310
311        assert_eq!(actual.as_slice().unwrap(), expected);
312    }
313
314    #[test]
315    fn unknown_symbol_fail() {
316        // DW: [":ref", "unknown"]
317        let pel = r#"[":ref", "0-7", "unknown"]"#;
318
319        let expression = Parser::new().parse_str(pel).unwrap();
320        let error = Runtime::new().eval(&expression).unwrap_err();
321
322        assert_eq!(
323            error.kind(),
324            &RuntimeErrorKind::UnknownSymbol("unknown".to_string())
325        );
326    }
327
328    #[test]
329    fn null_value() {
330        // DW: null
331        let pel = r#"[":null", "0-4"]"#;
332
333        let expression = Parser::new().parse_str(pel).unwrap();
334        let result = Runtime::new()
335            .eval(&expression)
336            .unwrap()
337            .complete()
338            .unwrap();
339
340        assert!(result.is_null());
341    }
342
343    #[test]
344    fn apply() {
345        // DW: "hi" ++ "by"
346        let json = r#"[":apply", "5-7", [":ref", "5-7", "++"], [":str", "0-4", "hi"], [":str", "8-12", "by"]]"#;
347
348        let expression = Parser::new().parse_str(json).unwrap();
349        let result = Runtime::new()
350            .eval(&expression)
351            .unwrap()
352            .complete()
353            .unwrap();
354
355        assert_eq!("hiby", result.as_str().unwrap());
356    }
357
358    #[test]
359    fn if_else() {
360        // DW: if (false) "a" else "b"
361        let json = r#"[":if", "0-23", [":bool", "4-9", "false"], [":str", "11-14", "a"], [":str", "20-23", "b"]]"#;
362
363        let expression = Parser::new().parse_str(json).unwrap();
364        let result = Runtime::new()
365            .eval(&expression)
366            .unwrap()
367            .complete()
368            .unwrap();
369
370        assert_eq!("b", result.as_str().unwrap());
371    }
372
373    #[test]
374    fn default_left_null() {
375        // DW: null default "right"
376        let pel = r#"
377            [":default", "0-6", 
378                [":null", "0-1"], 
379                [":str", "5-6", "right"]
380            ]
381        "#;
382
383        let expression = Parser::new().parse_str(pel).unwrap();
384        let result = Runtime::new()
385            .eval(&expression)
386            .unwrap()
387            .complete()
388            .unwrap();
389
390        assert_eq!(result.as_str().unwrap(), "right");
391    }
392
393    #[test]
394    fn default_left_not_null() {
395        // DW: 700 default true
396        let pel = r#"
397            [":default", "0-6", 
398                [":nbr", "0-1", "700"], 
399                [":bool", "5-6", "true"]
400            ]
401        "#;
402
403        let expression = Parser::new().parse_str(pel).unwrap();
404        let result = Runtime::new()
405            .eval(&expression)
406            .unwrap()
407            .complete()
408            .unwrap();
409
410        assert_eq!(result.as_f64().unwrap(), 700.0);
411    }
412
413    #[test]
414    fn array_positive_index() {
415        // DW: ["accept","accept-encoding","user-agent"][2]
416        let pel = r#"
417            [".", "0-45", 
418                [":array", "0-41", 
419                    [":str", "1-9", "accept"], 
420                    [":str", "10-27", "accept-encoding"], 
421                    [":str", "28-40", "user-agent"]
422                ], [":nbr", "42-44", "2"]
423            ]"#;
424
425        let expression = Parser::new().parse_str(pel).unwrap();
426        let result = Runtime::new()
427            .eval(&expression)
428            .unwrap()
429            .complete()
430            .unwrap();
431
432        assert_eq!("user-agent", result.as_str().unwrap());
433    }
434
435    #[test]
436    fn array_negative_index() {
437        // DW: ["accept","accept-encoding","user-agent"][-2]
438        let pel = r#"
439            [".", "0-45", 
440                [":array", "0-41", 
441                    [":str", "1-9", "accept"], 
442                    [":str", "10-27", "accept-encoding"], 
443                    [":str", "28-40", "user-agent"]
444                ], [":nbr", "42-44", "-2"]
445            ]"#;
446
447        let expression = Parser::new().parse_str(pel).unwrap();
448        let result = Runtime::new()
449            .eval(&expression)
450            .unwrap()
451            .complete()
452            .unwrap();
453
454        assert_eq!("accept-encoding", result.as_str().unwrap());
455    }
456
457    #[test]
458    fn array_out_of_bounds_index() {
459        // DW: ["accept","accept-encoding","user-agent"][5]
460        let pel = r#"
461            [".", "0-45", 
462                [":array", "0-41", 
463                    [":str", "1-9", "accept"], 
464                    [":str", "10-27", "accept-encoding"], 
465                    [":str", "28-40", "user-agent"]
466                ], [":nbr", "42-44", "5"]
467            ]"#;
468
469        let expression = Parser::new().parse_str(pel).unwrap();
470        let result = Runtime::new()
471            .eval(&expression)
472            .unwrap()
473            .complete()
474            .unwrap();
475
476        assert!(result.is_null());
477    }
478
479    #[test]
480    fn string_positive_index() {
481        // DW: "peregrine expression language"[10]
482        let pel = r#"
483            [".", "0-45", 
484                [":str", "0-41", "peregrine expression language"], 
485                [":nbr", "42-44", "10"]
486            ]"#;
487
488        let expression = Parser::new().parse_str(pel).unwrap();
489        let result = Runtime::new()
490            .eval(&expression)
491            .unwrap()
492            .complete()
493            .unwrap();
494
495        assert_eq!("e", result.as_str().unwrap());
496    }
497
498    #[test]
499    fn string_negative_index() {
500        // DW: "peregrine expression language"[-5]
501        let pel = r#"
502            [".", "0-45", 
503                [":str", "0-41", "peregrine expression language"], 
504                [":nbr", "42-44", "-5"]
505            ]"#;
506
507        let expression = Parser::new().parse_str(pel).unwrap();
508        let result = Runtime::new()
509            .eval(&expression)
510            .unwrap()
511            .complete()
512            .unwrap();
513
514        assert_eq!("g", result.as_str().unwrap());
515    }
516
517    #[test]
518    fn string_out_of_bounds_index() {
519        // DW: "peregrine expression language"[100]
520        let pel = r#"
521            [".", "0-45", 
522                [":str", "0-41", "peregrine expression language"], 
523                [":nbr", "42-44", "100"]
524            ]"#;
525
526        let expression = Parser::new().parse_str(pel).unwrap();
527        let result = Runtime::new()
528            .eval(&expression)
529            .unwrap()
530            .complete()
531            .unwrap();
532
533        assert!(result.is_null());
534    }
535
536    #[test]
537    fn selection_by_key() {
538        // Context
539        let mut b_object = Object::new();
540        b_object.insert("c".to_string(), Value::string("foo".to_string()));
541
542        let mut a_object = Object::new();
543        a_object.insert("b".to_string(), Value::object(b_object));
544
545        let a_object = Value::object(a_object);
546
547        let mut context = HashMap::new();
548        context.insert("a", a_object);
549
550        // DW: a.b.c
551        let json = r#"[".", "0-5", [".", "0-3", [":ref", "0-1", "a"], [":str", "2-3", "b"]], [":str", "4-5", "c"]]"#;
552
553        let expression = Parser::new().parse_str(json).unwrap();
554        let result = Runtime::new()
555            .eval_with_context(&expression, &context)
556            .unwrap()
557            .complete()
558            .unwrap();
559
560        assert_eq!("foo", result.as_str().unwrap());
561    }
562
563    #[test]
564    fn selection_by_key_in_array() {
565        // Context
566        let mut b_object = Object::new();
567        b_object.insert("c".to_string(), Value::string("foo".to_string()));
568
569        let mut a_object = Object::new();
570        a_object.insert("b".to_string(), Value::object(b_object));
571
572        let a_object = Value::object(a_object);
573
574        let array = Value::array(vec![a_object]);
575
576        let mut context = HashMap::new();
577        context.insert("array", array);
578
579        // DW: array.b.c
580        let pel = r#"
581            [".", "0-3", 
582                [".", "0-5", 
583                    [":ref", "0-5", "array"],
584                    [":str", "0-1", "b"]
585                ],
586                [":str", "2-3", "c"]
587            ]
588        "#;
589
590        let expression = Parser::new().parse_str(pel).unwrap();
591        let result = Runtime::new()
592            .eval_with_context(&expression, &context)
593            .unwrap()
594            .complete()
595            .unwrap();
596
597        assert_eq!(&[Value::string("foo")], result.as_slice().unwrap());
598    }
599
600    #[test]
601    fn select_by_key_inexistent_in_object() {
602        // Context
603        let b_object = Object::new();
604
605        let mut a_object = Object::new();
606        a_object.insert("b".to_string(), Value::object(b_object));
607
608        let a_object = Value::object(a_object);
609
610        let mut context = HashMap::new();
611        context.insert("a", a_object);
612
613        // DW: a.b.inexistent
614        let pel = r#"
615            [".", "0-5", 
616                [".", "0-3", 
617                    [":ref", "0-1", "a"], 
618                    [":str", "2-3", "b"]
619                ], 
620                [":str", "4-5", "inexistent"]
621            ]
622        "#;
623
624        let expression = Parser::new().parse_str(pel).unwrap();
625        let result = Runtime::new()
626            .eval_with_context(&expression, &context)
627            .unwrap()
628            .complete()
629            .unwrap();
630
631        assert!(result.is_null());
632    }
633
634    #[test]
635    fn select_by_key_inexistent_in_reference() {
636        struct TestContext;
637        struct TestValueHandler;
638
639        const CONTEXT_ID: ContextId = ContextId::new("TestContext");
640        const REFERENCE: Reference = CONTEXT_ID.first_reference();
641
642        impl ValueHandler for TestValueHandler {
643            fn detach(&self) -> Option<Value> {
644                unimplemented!()
645            }
646
647            fn select_by_key(&self, _key: &str) -> Option<Value> {
648                None
649            }
650        }
651
652        impl Context for TestContext {
653            fn resolve(&self, symbol: &Symbol) -> Binding {
654                match symbol.as_str() {
655                    "a" => Binding::Available(Value::reference(REFERENCE)),
656                    _ => Binding::Unknown,
657                }
658            }
659
660            fn value_handler(&self, reference: Reference) -> Option<&dyn ValueHandler> {
661                match reference {
662                    REFERENCE => Some(&TestValueHandler),
663                    _ => None,
664                }
665            }
666        }
667
668        // DW: a.b.inexistent
669        let pel = r#"
670            [".", "0-5", 
671                [":ref", "0-1", "a"], 
672                [":str", "2-3", "inexistent"]
673            ]
674        "#;
675
676        let expression = Parser::new().parse_str(pel).unwrap();
677        let result = Runtime::new()
678            .eval_with_context(&expression, &TestContext)
679            .unwrap()
680            .complete()
681            .unwrap();
682
683        assert!(result.is_null());
684    }
685
686    #[test]
687    fn operation_eq() {
688        // DW: 10 == 10.0
689        let json = r#"["==", "0-6", [":nbr", "0-1", "10"], [":nbr", "5-6", "10.0"]]"#;
690
691        let expression = Parser::new().parse_str(json).unwrap();
692        let result = Runtime::new()
693            .eval(&expression)
694            .unwrap()
695            .complete()
696            .unwrap();
697
698        assert!(result.as_bool().unwrap());
699    }
700
701    #[test]
702    fn operation_eq_mismatch() {
703        // DW: 10 == "10.0"
704        let json = r#"["==", "0-6", [":nbr", "0-1", "10"], [":str", "5-6", "10.0"]]"#;
705
706        let expression = Parser::new().parse_str(json).unwrap();
707        let result = Runtime::new()
708            .eval(&expression)
709            .unwrap()
710            .complete()
711            .unwrap();
712
713        assert!(!result.as_bool().unwrap());
714    }
715
716    #[test]
717    fn operation_neq() {
718        // DW: 10 != 10.0
719        let json = r#"["!=", "0-6", [":nbr", "0-1", "10"], [":nbr", "5-6", "10.0"]]"#;
720
721        let expression = Parser::new().parse_str(json).unwrap();
722        let result = Runtime::new()
723            .eval(&expression)
724            .unwrap()
725            .complete()
726            .unwrap();
727
728        assert!(!result.as_bool().unwrap());
729    }
730
731    #[test]
732    fn operation_neq_mismatch() {
733        // DW: 10 != "10"
734        let json = r#"["!=", "0-6", [":nbr", "0-1", "10"], [":str", "5-6", "10"]]"#;
735
736        let expression = Parser::new().parse_str(json).unwrap();
737        let result = Runtime::new()
738            .eval(&expression)
739            .unwrap()
740            .complete()
741            .unwrap();
742
743        assert!(result.as_bool().unwrap());
744    }
745
746    #[test]
747    fn operation_gt() {
748        // DW: 10 > 100.0
749        let json = r#"[">", "0-6", [":nbr", "0-1", "10"], [":nbr", "5-6", "100.0"]]"#;
750
751        let expression = Parser::new().parse_str(json).unwrap();
752        let result = Runtime::new()
753            .eval(&expression)
754            .unwrap()
755            .complete()
756            .unwrap();
757
758        assert!(!result.as_bool().unwrap());
759    }
760
761    #[test]
762    fn operation_get() {
763        // DW: "left" >= "left"
764        let json = r#"[">=", "0-6", [":str", "0-1", "left"], [":str", "5-6", "left"]]"#;
765
766        let expression = Parser::new().parse_str(json).unwrap();
767        let result = Runtime::new()
768            .eval(&expression)
769            .unwrap()
770            .complete()
771            .unwrap();
772
773        assert!(result.as_bool().unwrap());
774    }
775
776    #[test]
777    fn operation_lt() {
778        // DW: "2.0" < 10
779        let json = r#"["<", "0-6", [":str", "0-1", "2.0"], [":nbr", "5-6", "10"]]"#;
780
781        let expression = Parser::new().parse_str(json).unwrap();
782        let result = Runtime::new()
783            .eval(&expression)
784            .unwrap()
785            .complete()
786            .unwrap();
787
788        assert!(result.as_bool().unwrap());
789    }
790
791    #[test]
792    fn operation_let() {
793        // DW: true <= "true"
794        let json = r#"["<=", "0-6", [":bool", "0-1", "true"], [":str", "5-6", "true"]]"#;
795
796        let expression = Parser::new().parse_str(json).unwrap();
797        let result = Runtime::new()
798            .eval(&expression)
799            .unwrap()
800            .complete()
801            .unwrap();
802
803        assert!(result.as_bool().unwrap());
804    }
805
806    #[test]
807    fn operation_type_mismatch_fail() {
808        // DW: "11.0" <= true
809        let json = r#"["<=", "0-14", [":str", "0-6", "11.0"], [":bool", "9-13", "true"]]"#;
810
811        let expression = Parser::new().parse_str(json).unwrap();
812        let error = Runtime::new().eval(&expression).err().unwrap();
813
814        assert_eq!(error.kind(), &RuntimeErrorKind::TypeMismatch);
815        assert_eq!(error.location().start, 0);
816        assert_eq!(error.location().end, 14);
817    }
818
819    #[test]
820    fn operation_and() {
821        // DW: false && true
822        let json = r#"["&&", "0-6", [":bool", "0-1", "false"], [":bool", "5-6", "true"]]"#;
823
824        let expression = Parser::new().parse_str(json).unwrap();
825        let result = Runtime::new()
826            .eval(&expression)
827            .unwrap()
828            .complete()
829            .unwrap();
830
831        assert!(!result.as_bool().unwrap());
832    }
833
834    #[test]
835    fn operation_or() {
836        // DW: false || true
837        let json = r#"["||", "0-6", [":bool", "0-1", "false"], [":bool", "5-6", "true"]]"#;
838
839        let expression = Parser::new().parse_str(json).unwrap();
840        let result = Runtime::new()
841            .eval(&expression)
842            .unwrap()
843            .complete()
844            .unwrap();
845
846        assert!(result.as_bool().unwrap());
847    }
848
849    #[test]
850    fn operation_not() {
851        // DW: !false
852        let json = r#"["!", "0-6", [":bool", "0-1", "false"]]"#;
853
854        let expression = Parser::new().parse_str(json).unwrap();
855        let result = Runtime::new()
856            .eval(&expression)
857            .unwrap()
858            .complete()
859            .unwrap();
860
861        assert!(result.as_bool().unwrap());
862    }
863
864    #[test]
865    fn lower_null() {
866        // DW: lower(null)
867        let pel = r#"
868            [":apply", "5-7", 
869                [":ref", "5-7", "lower"], 
870                [":null", "0-4"]
871            ]
872        "#;
873
874        let expression = Parser::new().parse_str(pel).unwrap();
875        let result = Runtime::new()
876            .eval(&expression)
877            .unwrap()
878            .complete()
879            .unwrap();
880
881        assert!(result.is_null());
882    }
883
884    #[test]
885    fn lower_string() {
886        // DW: lower("Peregrine Expression Language")
887        let pel = r#"
888            [":apply", "5-7", 
889                [":ref", "5-7", "lower"], 
890                [":str", "0-4", "Peregrine Expression Language"]
891            ]
892        "#;
893
894        let expression = Parser::new().parse_str(pel).unwrap();
895        let result = Runtime::new()
896            .eval(&expression)
897            .unwrap()
898            .complete()
899            .unwrap();
900
901        assert_eq!("peregrine expression language", result.as_str().unwrap());
902    }
903
904    #[test]
905    fn lower_boolean() {
906        // DW: lower(true)
907        let pel = r#"
908            [":apply", "5-7", 
909                [":ref", "5-7", "lower"], 
910                [":bool", "0-4", "true"]
911            ]
912        "#;
913
914        let expression = Parser::new().parse_str(pel).unwrap();
915        let result = Runtime::new()
916            .eval(&expression)
917            .unwrap()
918            .complete()
919            .unwrap();
920
921        assert_eq!("true", result.as_str().unwrap());
922    }
923
924    #[test]
925    fn lower_number() {
926        // DW: lower(1944.07)
927        let pel = r#"
928            [":apply", "5-7", 
929                [":ref", "5-7", "lower"], 
930                [":nbr", "0-4", "1944.07"]
931            ]
932        "#;
933
934        let expression = Parser::new().parse_str(pel).unwrap();
935        let result = Runtime::new()
936            .eval(&expression)
937            .unwrap()
938            .complete()
939            .unwrap();
940
941        assert_eq!("1944.07", result.as_str().unwrap());
942    }
943
944    #[test]
945    fn lower_array_fail() {
946        // DW: lower(["accept", "accept-encoding", "user-agent"])
947        let pel = r#"
948            [":apply", "0-50", 
949                [":ref", "0-5", "lower"], 
950                [":array", "6-49", 
951                    [":str", "7-15", "accept"], 
952                    [":str", "17-34", "accept-encoding"], 
953                    [":str", "36-48", "user-agent"]
954                ]
955            ]
956        "#;
957
958        let expression = Parser::new().parse_str(pel).unwrap();
959        let error = Runtime::new().eval(&expression).unwrap_err();
960
961        assert_eq!(error.kind(), &RuntimeErrorKind::TypeMismatch);
962        assert_eq!(error.location().start, 0);
963        assert_eq!(error.location().end, 50);
964    }
965
966    #[test]
967    fn selection_by_key_in_lookup_context() {
968        struct LookupValueHandler(fn(&str) -> Option<Value>);
969        struct LookupContext;
970
971        const CONTEXT_ID: ContextId = ContextId::new("lookup_context");
972        const A_REFERENCE: Reference = CONTEXT_ID.first_reference();
973        const B_REFERENCE: Reference = A_REFERENCE.next();
974
975        static A_VALUE_HANDLER: LookupValueHandler =
976            LookupValueHandler(|key| (key == "d").then(|| Value::reference(B_REFERENCE)));
977        static B_VALUE_HANDLER: LookupValueHandler =
978            LookupValueHandler(|key| (key == "c").then(|| Value::number(111.0)));
979
980        impl ValueHandler for LookupValueHandler {
981            fn select_by_key(&self, key: &str) -> Option<Value> {
982                self.0(key)
983            }
984
985            fn detach(&self) -> Option<Value> {
986                None
987            }
988        }
989
990        impl Context for LookupContext {
991            fn resolve(&self, symbol: &Symbol) -> Binding {
992                match symbol.as_str() {
993                    "a" => Binding::Available(Value::reference(A_REFERENCE)),
994                    _ => Binding::Unknown,
995                }
996            }
997
998            fn value_handler(
999                &self,
1000                reference: Reference,
1001            ) -> Option<&dyn crate::runtime::ValueHandler> {
1002                match reference {
1003                    A_REFERENCE => Some(&A_VALUE_HANDLER),
1004                    B_REFERENCE => Some(&B_VALUE_HANDLER),
1005                    _ => None,
1006                }
1007            }
1008        }
1009
1010        // DW: a.d.c
1011        let json = r#"[".", "0-5", [".", "0-3", [":ref", "0-1", "a"], [":str", "2-3", "d"]], [":str", "4-5", "c"]]"#;
1012
1013        let expression = Parser::new().parse_str(json).unwrap();
1014        let result = Runtime::new()
1015            .eval_with_context(&expression, &LookupContext)
1016            .unwrap()
1017            .complete()
1018            .unwrap();
1019
1020        assert_eq!(111, result.as_f64().unwrap() as i32);
1021    }
1022
1023    #[test]
1024    fn contains_string_string_is_true() {
1025        // DW: contains("Peregrine expression language", "lang")
1026        let pel = r#"
1027            [":apply", "5-7", 
1028                [":ref", "5-8", "contains"],
1029                [":str", "5-7", "Peregrine expression language"], 
1030                [":str", "0-4", "lang"]
1031            ]
1032        "#;
1033
1034        let expression = Parser::new().parse_str(pel).unwrap();
1035        let result = Runtime::new()
1036            .eval(&expression)
1037            .unwrap()
1038            .complete()
1039            .unwrap();
1040
1041        assert!(result.as_bool().unwrap());
1042    }
1043
1044    #[test]
1045    fn contains_string_number_is_true() {
1046        // DW: contains("Peregrine expression language 1.0", 1.0)
1047        let pel = r#"
1048            [":apply", "5-7", 
1049                [":ref", "5-8", "contains"],
1050                [":str", "5-7", "Peregrine expression language 1.0"], 
1051                [":nbr", "0-4", "1.0"]
1052            ]
1053        "#;
1054
1055        let expression = Parser::new().parse_str(pel).unwrap();
1056        let result = Runtime::new()
1057            .eval(&expression)
1058            .unwrap()
1059            .complete()
1060            .unwrap();
1061
1062        assert!(result.as_bool().unwrap());
1063    }
1064
1065    #[test]
1066    fn contains_string_string_is_false() {
1067        // DW: contains("Peregrine expression language", "PEREGRINE")
1068        let pel = r#"
1069            [":apply", "5-7", 
1070                [":ref", "5-8", "contains"],
1071                [":str", "5-7", "Peregrine expression language"], 
1072                [":str", "0-4", "PEREGRINE"]
1073            ]
1074        "#;
1075
1076        let expression = Parser::new().parse_str(pel).unwrap();
1077        let result = Runtime::new()
1078            .eval(&expression)
1079            .unwrap()
1080            .complete()
1081            .unwrap();
1082
1083        assert!(!result.as_bool().unwrap());
1084    }
1085
1086    #[test]
1087    fn contains_boolean_string_is_true() {
1088        // DW: contains(true, "ru")
1089        let pel = r#"
1090            [":apply", "5-7", 
1091                [":ref", "5-8", "contains"],
1092                [":bool", "5-7", "true"], 
1093                [":str", "0-4", "ru"]
1094            ]
1095        "#;
1096
1097        let expression = Parser::new().parse_str(pel).unwrap();
1098        let result = Runtime::new()
1099            .eval(&expression)
1100            .unwrap()
1101            .complete()
1102            .unwrap();
1103
1104        assert!(result.as_bool().unwrap());
1105    }
1106
1107    #[test]
1108    fn contains_boolean_string_is_false() {
1109        // DW: contains(true, "fal")
1110        let pel = r#"
1111            [":apply", "5-7", 
1112                [":ref", "5-8", "contains"],
1113                [":bool", "5-7", "true"], 
1114                [":str", "0-4", "fal"]
1115            ]
1116        "#;
1117
1118        let expression = Parser::new().parse_str(pel).unwrap();
1119        let result = Runtime::new()
1120            .eval(&expression)
1121            .unwrap()
1122            .complete()
1123            .unwrap();
1124
1125        assert!(!result.as_bool().unwrap());
1126    }
1127
1128    #[test]
1129    fn contains_number_string_is_true() {
1130        // DW: contains(3.141516, ".141")
1131        let pel = r#"
1132            [":apply", "5-7", 
1133                [":ref", "5-8", "contains"],
1134                [":nbr", "5-7", "3.141516"], 
1135                [":str", "0-4", ".141"]
1136            ]
1137        "#;
1138
1139        let expression = Parser::new().parse_str(pel).unwrap();
1140        let result = Runtime::new()
1141            .eval(&expression)
1142            .unwrap()
1143            .complete()
1144            .unwrap();
1145
1146        assert!(result.as_bool().unwrap());
1147    }
1148
1149    #[test]
1150    fn contains_number_number_is_false() {
1151        // DW: contains(3.141516, 9)
1152        let pel = r#"
1153            [":apply", "5-7", 
1154                [":ref", "5-8", "contains"],
1155                [":nbr", "5-7", "3.141516"], 
1156                [":nbr", "0-4", "9"]
1157            ]
1158        "#;
1159
1160        let expression = Parser::new().parse_str(pel).unwrap();
1161        let result = Runtime::new()
1162            .eval(&expression)
1163            .unwrap()
1164            .complete()
1165            .unwrap();
1166
1167        assert!(!result.as_bool().unwrap());
1168    }
1169
1170    #[test]
1171    fn contains_array_is_true() {
1172        // DW: contains(["a", 10], 10)
1173        let pel = r#"
1174            [":apply", "5-7", 
1175                [":ref", "5-8", "contains"],
1176                [":array", "5-7", 
1177                    [":str", "5-7", "a"],
1178                    [":nbr", "5-7", "10"]
1179                ], 
1180                [":nbr", "0-4", "10"]
1181            ]
1182        "#;
1183
1184        let expression = Parser::new().parse_str(pel).unwrap();
1185        let result = Runtime::new()
1186            .eval(&expression)
1187            .unwrap()
1188            .complete()
1189            .unwrap();
1190
1191        assert!(result.as_bool().unwrap());
1192    }
1193
1194    #[test]
1195    fn contains_array_is_false() {
1196        // DW: contains(["a", false], 10)
1197        let pel = r#"
1198            [":apply", "5-7", 
1199                [":ref", "5-8", "contains"],
1200                [":array", "5-7", 
1201                    [":str", "5-7", "a"],
1202                    [":bool", "5-7", "false"]
1203                ], 
1204                [":nbr", "0-4", "10"]
1205            ]
1206        "#;
1207
1208        let expression = Parser::new().parse_str(pel).unwrap();
1209        let result = Runtime::new()
1210            .eval(&expression)
1211            .unwrap()
1212            .complete()
1213            .unwrap();
1214
1215        assert!(!result.as_bool().unwrap());
1216    }
1217
1218    #[test]
1219    fn trim_array() {
1220        // DW: trim(["one", 2, false])
1221        let pel = r#"
1222            [":apply", "5-7", 
1223                [":ref", "5-7", "trim"], 
1224                [":array", "0-17", 
1225                    [":str", "1-6", "one"], 
1226                    [":nbr", "8-9", "2"], 
1227                    [":bool", "11-16", "false"]
1228                ]
1229            ]
1230        "#;
1231
1232        let expression = Parser::new().parse_str(pel).unwrap();
1233        let error = Runtime::new().eval(&expression).unwrap_err();
1234
1235        assert_eq!(error.kind, RuntimeErrorKind::TypeMismatch);
1236    }
1237
1238    #[test]
1239    fn trim_boolean() {
1240        // DW: trim(true)
1241        let json = r#"
1242            [":apply", "5-7", 
1243                [":ref", "5-7", "trim"], 
1244                [":bool", "0-4", "true"]
1245            ]
1246        "#;
1247
1248        let expression = Parser::new().parse_str(json).unwrap();
1249        let result = Runtime::new()
1250            .eval(&expression)
1251            .unwrap()
1252            .complete()
1253            .unwrap();
1254
1255        assert_eq!("true", result.as_str().unwrap());
1256    }
1257
1258    #[test]
1259    fn trim_number() {
1260        // DW: trim(1000.0)
1261        let json = r#"
1262            [":apply", "5-7", 
1263                [":ref", "5-7", "trim"], 
1264                [":nbr", "0-4", "1000.0"]
1265            ]
1266        "#;
1267
1268        let expression = Parser::new().parse_str(json).unwrap();
1269        let result = Runtime::new()
1270            .eval(&expression)
1271            .unwrap()
1272            .complete()
1273            .unwrap();
1274
1275        // TODO: AGW-5356 - This should return "1000" when improve number coercion
1276        assert_eq!("1000.0", result.as_str().unwrap());
1277    }
1278
1279    #[test]
1280    fn trim_string() {
1281        // DW: trim("   hello world   ")
1282        let json = r#"
1283            [":apply", "5-7", 
1284                [":ref", "5-7", "trim"], 
1285                [":str", "0-4", "   hello world   "]
1286            ]
1287        "#;
1288
1289        let expression = Parser::new().parse_str(json).unwrap();
1290        let result = Runtime::new()
1291            .eval(&expression)
1292            .unwrap()
1293            .complete()
1294            .unwrap();
1295
1296        assert_eq!("hello world", result.as_str().unwrap());
1297    }
1298
1299    #[test]
1300    fn generate_uuid() {
1301        // DW: uuid()
1302        let pel = r#"
1303            [":apply", "5-7", 
1304                [":ref", "5-7", "uuid"]
1305            ]
1306        "#;
1307
1308        let expression = Parser::new().parse_str(pel).unwrap();
1309        let result = Runtime::new()
1310            .eval(&expression)
1311            .unwrap()
1312            .complete()
1313            .unwrap();
1314
1315        let uuid = result.as_str().unwrap();
1316
1317        assert!(uuid::Uuid::parse_str(uuid).is_ok());
1318    }
1319
1320    #[test]
1321    fn size_of_string() {
1322        // DW: sizeOf("hello world")
1323        let pel = r#"
1324            [":apply", "0-45", 
1325                [":ref", "0-10", "sizeOf"],
1326                [":str", "0-41", "hello world"]
1327            ]"#;
1328
1329        let expression = Parser::new().parse_str(pel).unwrap();
1330        let result = Runtime::new()
1331            .eval(&expression)
1332            .unwrap()
1333            .complete()
1334            .unwrap();
1335
1336        assert_eq!(11, result.as_f64().unwrap() as usize);
1337    }
1338
1339    #[test]
1340    fn size_of_array() {
1341        // DW: sizeOf(["accept", "accept-encoding", "user-agent"])
1342        let pel = r#"
1343            [":apply", "0-45", 
1344                [":ref", "0-10", "sizeOf"],
1345                [":array", "0-41", 
1346                    [":str", "1-9", "accept"], 
1347                    [":str", "10-27", "accept-encoding"], 
1348                    [":str", "28-40", "user-agent"]
1349                ]
1350            ]"#;
1351
1352        let expression = Parser::new().parse_str(pel).unwrap();
1353        let result = Runtime::new()
1354            .eval(&expression)
1355            .unwrap()
1356            .complete()
1357            .unwrap();
1358
1359        assert_eq!(3, result.as_f64().unwrap() as usize);
1360    }
1361
1362    #[test]
1363    fn size_of_object() {
1364        let object = Value::object(Object::from([("foo".to_string(), Value::bool(true))]));
1365
1366        let context = HashMap::from([("object", object)]);
1367
1368        // DW: sizeOf(object)
1369        let pel = r#"
1370            [":apply", "0-45", 
1371                [":ref", "0-10", "sizeOf"],
1372                [":ref", "0-41", "object"]
1373            ]"#;
1374
1375        let expression = Parser::new().parse_str(pel).unwrap();
1376        let result = Runtime::new()
1377            .eval_with_context(&expression, &context)
1378            .unwrap()
1379            .complete()
1380            .unwrap();
1381
1382        assert_eq!(1, result.as_f64().unwrap() as usize);
1383    }
1384
1385    #[test_case(r#"[":apply", "0-45", [":ref", "0-10", "isEmpty"],[":str", "0-41", ""]]"#,
1386        true;
1387        r#"DW isEmpty('') should be true 1"#
1388    )]
1389    #[test_case(r#"[":apply", "0-45", [":ref", "0-10", "isEmpty"],[":str", "0-41", "peregrine"]]"#,
1390        false;
1391        r#"DW isEmpty('peregrine') should be false"#
1392    )]
1393    #[test_case(r#"[":apply", "0-45", [":ref", "0-10", "isEmpty"],[":array", "0-2"]]"#,
1394        true;
1395        r#"DW isEmpty([]) should be true 2"#
1396    )]
1397    #[test_case(r#"[":apply", "0-45", [":ref", "0-10", "isEmpty"],[":array", "0-41", [":str", "1-9", "accept"]]]"#,
1398        false;
1399        r#"DW isEmpty(['accept']) should be false"#
1400    )]
1401    #[test_case(r#"[":apply", "0-45", [":ref", "0-10", "isEmpty"],[":ref", "0-41", "empty_object"]]"#,
1402        true;
1403        r#"DW isEmpty({}) should be true 3"#
1404    )]
1405    #[test_case(r#"[":apply", "0-45", [":ref", "0-10", "isEmpty"],[":ref", "0-41", "object"]]"#,
1406        false;
1407        r#"DW isEmpty({'foo': true}) should be false"#
1408    )]
1409    fn is_empty(pel: &str, expected: bool) {
1410        let object = Value::object(Object::from([("foo".to_string(), Value::bool(true))]));
1411        let empty_object = Value::object(Object::from([]));
1412        let context = HashMap::from([("object", object), ("empty_object", empty_object)]);
1413
1414        let expression = Parser::new().parse_str(pel).unwrap();
1415        let result = Runtime::new()
1416            .eval_with_context(&expression, &context)
1417            .unwrap()
1418            .complete()
1419            .unwrap();
1420
1421        assert_eq!(expected, result.as_bool().unwrap());
1422    }
1423
1424    #[test_case(r#"[":apply", "0-78", [":ref", "0-29", "toString"], [":apply", "30-69", [":ref", "30-60", "fromBase64"], [":str", "61-68", "SGk=="]], [":str", "70-77", "UTF-8"]]"#;
1425        r#"Double padded expression should return 'Hi'"#
1426    )]
1427    #[test_case(r#"[":apply", "0-77", [":ref", "0-29", "toString"], [":apply", "30-68", [":ref", "30-60", "fromBase64"], [":str", "61-67", "SGk="]], [":str", "69-76", "UTF-8"]]"#;
1428        r#"Padded expression should return 'Hi'"#
1429    )]
1430    #[test_case(r#"[":apply", "0-76", [":ref", "0-29", "toString"], [":apply", "30-68", [":ref", "30-60", "fromBase64"], [":str", "61-66", "SGk"]], [":str", "68-75", "UTF-8"]]"#;
1431        r#"Unpadded expression should return 'Hi'"#
1432    )]
1433    fn base64_is_decoded_padded_or_unpadded(pel: &str) {
1434        let headers = Value::object(HashMap::new());
1435        let context: Prelude = HashMap::from([("payload", headers)]);
1436
1437        let value = Runtime::new()
1438            .eval_with_context(&Parser::new().parse_str(pel).unwrap(), &context)
1439            .unwrap()
1440            .complete()
1441            .unwrap();
1442
1443        assert_eq!(value.as_str().unwrap(), "Hi");
1444    }
1445
1446    #[test]
1447    fn split_by_string_string() {
1448        // DW: splitBy("Peregrine Expression Language", "re")
1449        let pel = r#"
1450            [":apply", "5-7", 
1451                [":ref", "5-7", "splitBy"], 
1452                [":str", "0-4", "Peregrine Expression Language"], 
1453                [":str", "8-12", "re"]
1454            ]
1455        "#;
1456
1457        let expression = Parser::new().parse_str(pel).unwrap();
1458        let result = Runtime::new()
1459            .eval(&expression)
1460            .unwrap()
1461            .complete()
1462            .unwrap();
1463
1464        let expected = [
1465            Value::string("Pe".to_string()),
1466            Value::string("grine Exp".to_string()),
1467            Value::string("ssion Language".to_string()),
1468        ];
1469
1470        assert_eq!(&expected, result.as_slice().unwrap());
1471    }
1472
1473    #[test]
1474    fn split_by_string_empty_string() {
1475        // DW: splitBy("2022", "")
1476        let pel = r#"
1477            [":apply", "5-7", 
1478                [":ref", "5-7", "splitBy"], 
1479                [":str", "0-4", "2022"], 
1480                [":str", "8-12", ""]
1481            ]
1482        "#;
1483
1484        let expression = Parser::new().parse_str(pel).unwrap();
1485        let result = Runtime::new()
1486            .eval(&expression)
1487            .unwrap()
1488            .complete()
1489            .unwrap();
1490
1491        let expected = [
1492            Value::string("2".to_string()),
1493            Value::string("0".to_string()),
1494            Value::string("2".to_string()),
1495            Value::string("2".to_string()),
1496        ];
1497
1498        assert_eq!(&expected, result.as_slice().unwrap());
1499    }
1500
1501    #[ignore = "bug W-11098754"]
1502    #[test]
1503    fn split_by_string_string_2() {
1504        // DW: splitBy("2022", "2")
1505        let pel = r#"
1506            [":apply", "5-7", 
1507                [":ref", "5-7", "splitBy"], 
1508                [":str", "0-4", "2022"], 
1509                [":str", "8-12", "2"]
1510            ]
1511        "#;
1512
1513        let expression = Parser::new().parse_str(pel).unwrap();
1514        let result = Runtime::new()
1515            .eval(&expression)
1516            .unwrap()
1517            .complete()
1518            .unwrap();
1519
1520        let expected = [
1521            Value::string("".to_string()),
1522            Value::string("0".to_string()),
1523        ];
1524
1525        assert_eq!(&expected, result.as_slice().unwrap());
1526    }
1527
1528    #[test]
1529    fn split_by_number_string() {
1530        // DW: splitBy(1946.03, ".")
1531        let pel = r#"
1532            [":apply", "5-7", 
1533                [":ref", "5-7", "splitBy"], 
1534                [":nbr", "0-4", "1946.03"], 
1535                [":str", "8-12", "."]
1536            ]
1537        "#;
1538
1539        let expression = Parser::new().parse_str(pel).unwrap();
1540        let result = Runtime::new()
1541            .eval(&expression)
1542            .unwrap()
1543            .complete()
1544            .unwrap();
1545
1546        let expected = [
1547            Value::string("1946".to_string()),
1548            Value::string("03".to_string()),
1549        ];
1550
1551        assert_eq!(&expected, result.as_slice().unwrap());
1552    }
1553
1554    #[test]
1555    fn split_by_boolean_string() {
1556        // DW: splitBy(true, "r")
1557        let pel = r#"
1558            [":apply", "5-7", 
1559                [":ref", "5-7", "splitBy"], 
1560                [":bool", "0-4", "true"], 
1561                [":str", "8-12", "r"]
1562            ]
1563        "#;
1564
1565        let expression = Parser::new().parse_str(pel).unwrap();
1566        let result = Runtime::new()
1567            .eval(&expression)
1568            .unwrap()
1569            .complete()
1570            .unwrap();
1571
1572        let expected = [
1573            Value::string("t".to_string()),
1574            Value::string("ue".to_string()),
1575        ];
1576
1577        assert_eq!(&expected, result.as_slice().unwrap());
1578    }
1579
1580    #[test]
1581    fn split_by_null_string() {
1582        // DW: splitBy(null, "re")
1583        let pel = r#"
1584            [":apply", "5-7", 
1585                [":ref", "5-7", "splitBy"], 
1586                [":null", "0-41"], 
1587                [":str", "8-12", "r"]
1588            ]
1589        "#;
1590
1591        let expression = Parser::new().parse_str(pel).unwrap();
1592        let result = Runtime::new()
1593            .eval(&expression)
1594            .unwrap()
1595            .complete()
1596            .unwrap();
1597
1598        assert!(result.is_null());
1599    }
1600
1601    #[test]
1602    fn split_by_string_null_faiil() {
1603        // DW: splitBy("Peregrine", null)
1604        let pel = r#"
1605            [":apply", "5-7", 
1606                [":ref", "5-7", "splitBy"], 
1607                [":str", "0-41", "Peregrine"], 
1608                [":null", "8-12"]
1609            ]
1610        "#;
1611
1612        let expression = Parser::new().parse_str(pel).unwrap();
1613        let error = Runtime::new().eval(&expression).unwrap_err();
1614
1615        assert_eq!(error.kind, RuntimeErrorKind::TypeMismatch);
1616    }
1617
1618    #[test]
1619    fn split_by_array_fail() {
1620        // DW: splitBy(["accept", "accept-encoding", "user-agent"], "re")
1621        let pel = r#"
1622            [":apply", "5-7", 
1623                [":ref", "5-7", "splitBy"], 
1624                [":array", "0-41", 
1625                    [":str", "1-9", "accept"], 
1626                    [":str", "10-27", "accept-encoding"], 
1627                    [":str", "28-40", "user-agent"]
1628                ], 
1629                [":str", "8-12", "r"]
1630            ]
1631        "#;
1632
1633        let expression = Parser::new().parse_str(pel).unwrap();
1634        let error = Runtime::new().eval(&expression).unwrap_err();
1635
1636        assert_eq!(error.kind, RuntimeErrorKind::TypeMismatch);
1637    }
1638
1639    #[test]
1640    fn substring_after_null_string() {
1641        // DW: substringAfter(null, "peregrine")
1642        let pel = r#"
1643            [":apply", "5-7", 
1644                [":ref", "5-7", "substringAfter"], 
1645                [":null", "0-4"], 
1646                [":str", "8-12", "peregrine"]
1647            ]"#;
1648
1649        let expression = Parser::new().parse_str(pel).unwrap();
1650        let result = Runtime::new()
1651            .eval(&expression)
1652            .unwrap()
1653            .complete()
1654            .unwrap();
1655
1656        assert!(result.is_null());
1657    }
1658
1659    #[test]
1660    fn substring_after_string_string() {
1661        // DW: substringAfter("peregrine", "gr")
1662        let pel = r#"
1663            [":apply", "5-7", 
1664                [":ref", "5-7", "substringAfter"], 
1665                [":str", "0-4", "peregrine"], 
1666                [":str", "8-12", "gr"]
1667            ]"#;
1668
1669        let expression = Parser::new().parse_str(pel).unwrap();
1670        let result = Runtime::new()
1671            .eval(&expression)
1672            .unwrap()
1673            .complete()
1674            .unwrap();
1675
1676        assert_eq!(result.as_str().unwrap(), "ine");
1677    }
1678
1679    #[test]
1680    fn substring_after_string_null_fail() {
1681        // DW: substringAfter("peregrine", null)
1682        let pel = r#"
1683            [":apply", "5-7", 
1684                [":ref", "5-7", "substringAfter"], 
1685                [":str", "0-4", "peregrine"], 
1686                [":null", "8-12"]
1687            ]"#;
1688
1689        let expression = Parser::new().parse_str(pel).unwrap();
1690        let error = Runtime::new().eval(&expression).unwrap_err();
1691
1692        assert_eq!(error.kind, RuntimeErrorKind::TypeMismatch);
1693    }
1694
1695    #[test]
1696    fn substring_after_boolean_string() {
1697        // DW: substringAfter(true, "r")
1698        let pel = r#"
1699            [":apply", "5-7", 
1700                [":ref", "5-7", "substringAfter"], 
1701                [":bool", "0-4", "true"], 
1702                [":str", "8-12", "r"]
1703            ]"#;
1704
1705        let expression = Parser::new().parse_str(pel).unwrap();
1706        let result = Runtime::new()
1707            .eval(&expression)
1708            .unwrap()
1709            .complete()
1710            .unwrap();
1711
1712        assert_eq!(result.as_str().unwrap(), "ue");
1713    }
1714
1715    #[test]
1716    fn substring_after_number_number() {
1717        // DW: substringAfter(1234.567, 4.5)
1718        let pel = r#"
1719            [":apply", "5-7", 
1720                [":ref", "5-7", "substringAfter"], 
1721                [":nbr", "0-4", "1234.567"], 
1722                [":nbr", "8-12", "4.5"]
1723            ]"#;
1724
1725        let expression = Parser::new().parse_str(pel).unwrap();
1726        let result = Runtime::new()
1727            .eval(&expression)
1728            .unwrap()
1729            .complete()
1730            .unwrap();
1731
1732        assert_eq!(result.as_str().unwrap(), "67");
1733    }
1734
1735    #[test]
1736    fn substring_after_array_fail() {
1737        // DW: substringAfter(["accept", "accept-encoding", "user-agent"], "re")
1738        let pel = r#"
1739            [":apply", "5-7", 
1740                [":ref", "5-7", "substringAfter"], 
1741                [":array", "0-41", 
1742                    [":str", "1-9", "accept"], 
1743                    [":str", "10-27", "accept-encoding"], 
1744                    [":str", "28-40", "user-agent"]
1745                ],
1746                [":str", "1-10", "re"]
1747            ]
1748        "#;
1749
1750        let expression = Parser::new().parse_str(pel).unwrap();
1751        let result = Runtime::new().eval(&expression).unwrap_err();
1752
1753        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
1754    }
1755
1756    #[test]
1757    fn substring_after_not_found() {
1758        // DW: substringAfter("peregrine", "XX")
1759        let pel = r#"
1760            [":apply", "5-7",
1761                [":ref", "5-7", "substringAfter"],
1762                [":str", "0-4", "peregrine"],
1763                [":str", "8-12", "XX"]
1764            ]"#;
1765
1766        let expression = Parser::new().parse_str(pel).unwrap();
1767        let result = Runtime::new()
1768            .eval(&expression)
1769            .unwrap()
1770            .complete()
1771            .unwrap();
1772
1773        assert_eq!(result.as_str().unwrap(), "");
1774    }
1775
1776    #[test]
1777    fn substring_after_empty_string() {
1778        // DW: substringAfter("peregrine", "")
1779        let pel = r#"
1780            [":apply", "5-7",
1781                [":ref", "5-7", "substringAfter"],
1782                [":str", "0-4", "peregrine"],
1783                [":str", "8-10", ""]
1784            ]"#;
1785
1786        let expression = Parser::new().parse_str(pel).unwrap();
1787        let result = Runtime::new()
1788            .eval(&expression)
1789            .unwrap()
1790            .complete()
1791            .unwrap();
1792
1793        assert_eq!(result.as_str().unwrap(), "peregrine");
1794    }
1795
1796    #[test]
1797    fn substring_after_last_null_string() {
1798        // DW: substringAfterLast(null, "peregrine")
1799        let pel = r#"
1800            [":apply", "5-7", 
1801                [":ref", "5-7", "substringAfterLast"], 
1802                [":null", "0-4"], 
1803                [":str", "8-12", "peregrine"]
1804            ]"#;
1805
1806        let expression = Parser::new().parse_str(pel).unwrap();
1807        let result = Runtime::new()
1808            .eval(&expression)
1809            .unwrap()
1810            .complete()
1811            .unwrap();
1812
1813        assert!(result.is_null());
1814    }
1815
1816    #[test]
1817    fn substring_after_last_string_string() {
1818        // DW: substringAfterLast("Peregrine Expression Language", "re")
1819        let pel = r#"
1820            [":apply", "5-7", 
1821                [":ref", "5-7", "substringAfterLast"], 
1822                [":str", "0-4", "Peregrine Expression Language"], 
1823                [":str", "8-12", "re"]
1824            ]"#;
1825
1826        let expression = Parser::new().parse_str(pel).unwrap();
1827        let result = Runtime::new()
1828            .eval(&expression)
1829            .unwrap()
1830            .complete()
1831            .unwrap();
1832
1833        assert_eq!(result.as_str().unwrap(), "ssion Language");
1834    }
1835
1836    #[test]
1837    fn substring_after_last_string_null_fail() {
1838        // DW: substringAfterLast("peregrine", null)
1839        let pel = r#"
1840            [":apply", "5-7", 
1841                [":ref", "5-7", "substringAfterLast"], 
1842                [":str", "0-4", "peregrine"], 
1843                [":null", "8-12"]
1844            ]"#;
1845
1846        let expression = Parser::new().parse_str(pel).unwrap();
1847        let error = Runtime::new().eval(&expression).unwrap_err();
1848
1849        assert_eq!(error.kind, RuntimeErrorKind::TypeMismatch);
1850    }
1851
1852    #[test]
1853    fn substring_after_last_boolean_string() {
1854        // DW: substringAfterLast(true, "r")
1855        let pel = r#"
1856            [":apply", "5-7", 
1857                [":ref", "5-7", "substringAfterLast"], 
1858                [":bool", "0-4", "true"], 
1859                [":str", "8-12", "r"]
1860            ]"#;
1861
1862        let expression = Parser::new().parse_str(pel).unwrap();
1863        let result = Runtime::new()
1864            .eval(&expression)
1865            .unwrap()
1866            .complete()
1867            .unwrap();
1868
1869        assert_eq!(result.as_str().unwrap(), "ue");
1870    }
1871
1872    #[test]
1873    fn substring_after_last_number_number() {
1874        // DW: substringAfterLast(12123512.3512, 35)
1875        let pel = r#"
1876            [":apply", "5-7", 
1877                [":ref", "5-7", "substringAfterLast"], 
1878                [":nbr", "0-4", "12123512.3512"], 
1879                [":nbr", "8-12", "35"]
1880            ]"#;
1881
1882        let expression = Parser::new().parse_str(pel).unwrap();
1883        let result = Runtime::new()
1884            .eval(&expression)
1885            .unwrap()
1886            .complete()
1887            .unwrap();
1888
1889        assert_eq!(result.as_str().unwrap(), "12");
1890    }
1891
1892    #[test]
1893    fn substring_after_last_array_fail() {
1894        // DW: substringAfterLast(["accept", "accept-encoding", "user-agent"], "re")
1895        let pel = r#"
1896            [":apply", "5-7", 
1897                [":ref", "5-7", "substringAfterLast"], 
1898                [":array", "0-41", 
1899                    [":str", "1-9", "accept"], 
1900                    [":str", "10-27", "accept-encoding"], 
1901                    [":str", "28-40", "user-agent"]
1902                ],
1903                [":str", "1-10", "re"]
1904            ]
1905        "#;
1906
1907        let expression = Parser::new().parse_str(pel).unwrap();
1908        let result = Runtime::new().eval(&expression).unwrap_err();
1909
1910        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
1911    }
1912
1913    #[test]
1914    fn substring_after_last_not_found() {
1915        // DW: substringAfter("peregrine", "XX")
1916        let pel = r#"
1917            [":apply", "5-7",
1918                [":ref", "5-7", "substringAfterLast"],
1919                [":str", "0-4", "peregrine"],
1920                [":str", "8-12", "XX"]
1921            ]"#;
1922
1923        let expression = Parser::new().parse_str(pel).unwrap();
1924        let result = Runtime::new()
1925            .eval(&expression)
1926            .unwrap()
1927            .complete()
1928            .unwrap();
1929
1930        assert_eq!(result.as_str().unwrap(), "");
1931    }
1932
1933    #[test]
1934    fn substring_after_last_empty_string() {
1935        // DW: substringAfter("peregrine", "")
1936        let pel = r#"
1937            [":apply", "5-7",
1938                [":ref", "5-7", "substringAfterLast"],
1939                [":str", "0-4", "peregrine"],
1940                [":str", "8-10", ""]
1941            ]"#;
1942
1943        let expression = Parser::new().parse_str(pel).unwrap();
1944        let result = Runtime::new()
1945            .eval(&expression)
1946            .unwrap()
1947            .complete()
1948            .unwrap();
1949
1950        assert_eq!(result.as_str().unwrap(), "");
1951    }
1952
1953    #[test]
1954    fn substring_before_null_string() {
1955        // DW: substringBefore(null, "peregrine")
1956        let pel = r#"
1957            [":apply", "5-7", 
1958                [":ref", "5-7", "substringBefore"], 
1959                [":null", "0-4"], 
1960                [":str", "8-12", "peregrine"]
1961            ]"#;
1962
1963        let expression = Parser::new().parse_str(pel).unwrap();
1964        let result = Runtime::new()
1965            .eval(&expression)
1966            .unwrap()
1967            .complete()
1968            .unwrap();
1969
1970        assert!(result.is_null());
1971    }
1972
1973    #[test]
1974    fn substring_before_string_string() {
1975        // DW: substringBefore("peregrine", "gr")
1976        let pel = r#"
1977            [":apply", "5-7", 
1978                [":ref", "5-7", "substringBefore"], 
1979                [":str", "0-4", "peregrine"], 
1980                [":str", "8-12", "gr"]
1981            ]"#;
1982
1983        let expression = Parser::new().parse_str(pel).unwrap();
1984        let result = Runtime::new()
1985            .eval(&expression)
1986            .unwrap()
1987            .complete()
1988            .unwrap();
1989
1990        assert_eq!(result.as_str().unwrap(), "pere");
1991    }
1992
1993    #[test]
1994    fn substring_before_string_null_fail() {
1995        // DW: substringBefore("peregrine", null)
1996        let pel = r#"
1997            [":apply", "5-7", 
1998                [":ref", "5-7", "substringBefore"], 
1999                [":str", "0-4", "peregrine"], 
2000                [":null", "8-12"]
2001            ]"#;
2002
2003        let expression = Parser::new().parse_str(pel).unwrap();
2004        let result = Runtime::new().eval(&expression).unwrap_err();
2005
2006        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
2007    }
2008
2009    #[test]
2010    fn substring_before_boolean_string() {
2011        // DW: substringBefore(true, "ue")
2012        let pel = r#"
2013            [":apply", "5-7", 
2014                [":ref", "5-7", "substringBefore"], 
2015                [":bool", "0-4", "true"], 
2016                [":str", "8-12", "ue"]
2017            ]"#;
2018
2019        let expression = Parser::new().parse_str(pel).unwrap();
2020        let result = Runtime::new()
2021            .eval(&expression)
2022            .unwrap()
2023            .complete()
2024            .unwrap();
2025
2026        assert_eq!(result.as_str().unwrap(), "tr");
2027    }
2028
2029    #[test]
2030    fn substring_before_number_number() {
2031        // DW: substringBefore(1234.56, 4.5)
2032        let pel = r#"
2033            [":apply", "5-7", 
2034                [":ref", "5-7", "substringBefore"], 
2035                [":nbr", "0-4", "1234.56"], 
2036                [":nbr", "8-12", "4.5"]
2037            ]"#;
2038
2039        let expression = Parser::new().parse_str(pel).unwrap();
2040        let result = Runtime::new()
2041            .eval(&expression)
2042            .unwrap()
2043            .complete()
2044            .unwrap();
2045
2046        assert_eq!(result.as_str().unwrap(), "123");
2047    }
2048
2049    #[test]
2050    fn substring_before_array_fail() {
2051        // DW: substringBefore(["accept", "accept-encoding", "user-agent"], "re")
2052        let pel = r#"
2053            [":apply", "5-7", 
2054                [":ref", "5-7", "substringBefore"], 
2055                [":array", "0-41", 
2056                    [":str", "1-9", "accept"], 
2057                    [":str", "10-27", "accept-encoding"], 
2058                    [":str", "28-40", "user-agent"]
2059                ],
2060                [":str", "1-10", "re"]
2061            ]
2062        "#;
2063
2064        let expression = Parser::new().parse_str(pel).unwrap();
2065        let result = Runtime::new().eval(&expression).unwrap_err();
2066
2067        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
2068    }
2069
2070    #[test]
2071    fn substring_before_not_found() {
2072        // DW: substringBefore("peregrine", "XX")
2073        let pel = r#"
2074            [":apply", "5-7",
2075                [":ref", "5-7", "substringBefore"],
2076                [":str", "0-4", "peregrine"],
2077                [":str", "8-12", "XX"]
2078            ]"#;
2079
2080        let expression = Parser::new().parse_str(pel).unwrap();
2081        let result = Runtime::new()
2082            .eval(&expression)
2083            .unwrap()
2084            .complete()
2085            .unwrap();
2086
2087        assert_eq!(result.as_str().unwrap(), "");
2088    }
2089
2090    #[test]
2091    fn substring_before_empty_string() {
2092        // DW: substringBefore("peregrine", "")
2093        let pel = r#"
2094            [":apply", "0-55",
2095                [":ref", "0-38", "substringBefore"],
2096                [":str", "39-50", "peregrine"],
2097                [":str", "52-54", ""]
2098            ]"#;
2099
2100        let expression = Parser::new().parse_str(pel).unwrap();
2101        let result = Runtime::new()
2102            .eval(&expression)
2103            .unwrap()
2104            .complete()
2105            .unwrap();
2106
2107        assert_eq!(result.as_str().unwrap(), "");
2108    }
2109
2110    #[test]
2111    fn substring_before_last_null_string() {
2112        // DW: substringBeforeLast(null, "peregrine")
2113        let pel = r#"
2114            [":apply", "5-7", 
2115                [":ref", "5-7", "substringBeforeLast"], 
2116                [":null", "0-4"], 
2117                [":str", "8-12", "peregrine"]
2118            ]"#;
2119
2120        let expression = Parser::new().parse_str(pel).unwrap();
2121        let result = Runtime::new()
2122            .eval(&expression)
2123            .unwrap()
2124            .complete()
2125            .unwrap();
2126
2127        assert!(result.is_null());
2128    }
2129
2130    #[test]
2131    fn substring_before_last_string_string() {
2132        // DW: substringBeforeLast("Peregrine Expression Language", "re")
2133        let pel = r#"
2134            [":apply", "5-7", 
2135                [":ref", "5-7", "substringBeforeLast"], 
2136                [":str", "0-4", "Peregrine Expression Language"], 
2137                [":str", "8-12", "re"]
2138            ]"#;
2139
2140        let expression = Parser::new().parse_str(pel).unwrap();
2141        let result = Runtime::new()
2142            .eval(&expression)
2143            .unwrap()
2144            .complete()
2145            .unwrap();
2146
2147        assert_eq!(result.as_str().unwrap(), "Peregrine Exp");
2148    }
2149
2150    #[test]
2151    fn substring_before_last_string_null_fail() {
2152        // DW: substringBeforeLast("peregrine", null)
2153        let pel = r#"
2154            [":apply", "5-7", 
2155                [":ref", "5-7", "substringBeforeLast"], 
2156                [":str", "0-4", "peregrine"], 
2157                [":null", "8-12"]
2158            ]"#;
2159
2160        let expression = Parser::new().parse_str(pel).unwrap();
2161        let result = Runtime::new().eval(&expression).unwrap_err();
2162
2163        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
2164    }
2165
2166    #[test]
2167    fn substring_before_last_boolean_string() {
2168        // DW: substringBeforeLast(true, "ue")
2169        let pel = r#"
2170            [":apply", "5-7", 
2171                [":ref", "5-7", "substringBeforeLast"], 
2172                [":bool", "0-4", "true"], 
2173                [":str", "8-12", "ue"]
2174            ]"#;
2175
2176        let expression = Parser::new().parse_str(pel).unwrap();
2177        let result = Runtime::new()
2178            .eval(&expression)
2179            .unwrap()
2180            .complete()
2181            .unwrap();
2182
2183        assert_eq!(result.as_str().unwrap(), "tr");
2184    }
2185
2186    #[test]
2187    fn substring_before_last_number_number() {
2188        // DW: substringBeforeLast(121235.123512, 4.5)
2189        let pel = r#"
2190            [":apply", "5-7", 
2191                [":ref", "5-7", "substringBeforeLast"], 
2192                [":nbr", "0-4", "121235.123512"], 
2193                [":nbr", "8-12", "12"]
2194            ]"#;
2195
2196        let expression = Parser::new().parse_str(pel).unwrap();
2197        let result = Runtime::new()
2198            .eval(&expression)
2199            .unwrap()
2200            .complete()
2201            .unwrap();
2202
2203        assert_eq!(result.as_str().unwrap(), "121235.1235");
2204    }
2205
2206    #[test]
2207    fn substring_before_last_array_fail() {
2208        // DW: substringBeforeLast(["accept", "accept-encoding", "user-agent"], "re")
2209        let pel = r#"
2210            [":apply", "5-7", 
2211                [":ref", "5-7", "substringBeforeLast"], 
2212                [":array", "0-41", 
2213                    [":str", "1-9", "accept"], 
2214                    [":str", "10-27", "accept-encoding"], 
2215                    [":str", "28-40", "user-agent"]
2216                ],
2217                [":str", "1-10", "re"]
2218            ]
2219        "#;
2220
2221        let expression = Parser::new().parse_str(pel).unwrap();
2222        let result = Runtime::new().eval(&expression).unwrap_err();
2223
2224        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
2225    }
2226
2227    #[test]
2228    fn substring_before_last_not_found() {
2229        // DW: substringBeforeLast("peregrine", "XX")
2230        let pel = r#"
2231            [":apply", "5-7",
2232                [":ref", "5-7", "substringBeforeLast"],
2233                [":str", "0-4", "peregrine"],
2234                [":str", "8-12", "XX"]
2235            ]"#;
2236
2237        let expression = Parser::new().parse_str(pel).unwrap();
2238        let result = Runtime::new()
2239            .eval(&expression)
2240            .unwrap()
2241            .complete()
2242            .unwrap();
2243
2244        assert_eq!(result.as_str().unwrap(), "");
2245    }
2246
2247    #[test]
2248    fn substring_before_last_empty_string() {
2249        // DW: substringBeforeLast("peregrine", "")
2250        let pel = r#"
2251            [":apply", "0-55",
2252                [":ref", "0-38", "substringBeforeLast"],
2253                [":str", "39-50", "peregrine"],
2254                [":str", "52-54", ""]
2255            ]"#;
2256
2257        let expression = Parser::new().parse_str(pel).unwrap();
2258        let result = Runtime::new()
2259            .eval(&expression)
2260            .unwrap()
2261            .complete()
2262            .unwrap();
2263
2264        assert_eq!(result.as_str().unwrap(), "peregrin");
2265    }
2266
2267    #[test]
2268    fn upper_null() {
2269        // DW: upper(null)
2270        let pel = r#"
2271            [":apply", "5-7", 
2272                [":ref", "5-7", "upper"], 
2273                [":null", "0-4"]
2274            ]
2275        "#;
2276
2277        let expression = Parser::new().parse_str(pel).unwrap();
2278        let result = Runtime::new()
2279            .eval(&expression)
2280            .unwrap()
2281            .complete()
2282            .unwrap();
2283
2284        assert!(result.is_null());
2285    }
2286
2287    #[test]
2288    fn upper_string() {
2289        // DW: upper("Peregrine Expression Language")
2290        let pel = r#"
2291            [":apply", "5-7", 
2292                [":ref", "5-7", "upper"], 
2293                [":str", "0-4", "Peregrine Expression Language"]
2294            ]
2295        "#;
2296
2297        let expression = Parser::new().parse_str(pel).unwrap();
2298        let result = Runtime::new()
2299            .eval(&expression)
2300            .unwrap()
2301            .complete()
2302            .unwrap();
2303
2304        assert_eq!("PEREGRINE EXPRESSION LANGUAGE", result.as_str().unwrap());
2305    }
2306
2307    #[test]
2308    fn upper_boolean() {
2309        // DW: upper(true)
2310        let pel = r#"
2311            [":apply", "5-7", 
2312                [":ref", "5-7", "upper"], 
2313                [":bool", "0-4", "true"]
2314            ]
2315        "#;
2316
2317        let expression = Parser::new().parse_str(pel).unwrap();
2318        let result = Runtime::new()
2319            .eval(&expression)
2320            .unwrap()
2321            .complete()
2322            .unwrap();
2323
2324        assert_eq!("TRUE", result.as_str().unwrap());
2325    }
2326
2327    #[test]
2328    fn upper_number() {
2329        // DW: upper(123.4)
2330        let pel = r#"
2331            [":apply", "5-7", 
2332                [":ref", "5-7", "upper"], 
2333                [":nbr", "0-4", "123.4"]
2334            ]
2335        "#;
2336
2337        let expression = Parser::new().parse_str(pel).unwrap();
2338        let result = Runtime::new()
2339            .eval(&expression)
2340            .unwrap()
2341            .complete()
2342            .unwrap();
2343
2344        assert_eq!("123.4", result.as_str().unwrap());
2345    }
2346
2347    #[test]
2348    fn upper_array_fail() {
2349        // DW: upper(["accept", "accept-encoding", "user-agent"], "re")
2350        let pel = r#"
2351            [":apply", "5-7", 
2352                [":ref", "5-7", "upper"], 
2353                [":array", "0-41", 
2354                    [":str", "1-9", "accept"], 
2355                    [":str", "10-27", "accept-encoding"], 
2356                    [":str", "28-40", "user-agent"]
2357                ]
2358            ]
2359        "#;
2360
2361        let expression = Parser::new().parse_str(pel).unwrap();
2362        let error = Runtime::new().eval(&expression).unwrap_err();
2363
2364        assert_eq!(error.kind, RuntimeErrorKind::TypeMismatch);
2365    }
2366
2367    #[test]
2368    fn to_binary() {
2369        // DW: dw::util::Coercions::toBinary("some", "UTF-8")
2370        let pel = r#"[":apply", "0-46", [":ref", "0-29", "toBinary"], [":str", "30-36", "some"], [":str", "38-45", "UTF-8"]]"#;
2371
2372        let expression = Parser::new().parse_str(pel).unwrap();
2373        let result = Runtime::new()
2374            .eval(&expression)
2375            .unwrap()
2376            .complete()
2377            .unwrap();
2378
2379        assert_eq!(result.as_binary().unwrap(), "some".as_bytes());
2380    }
2381
2382    #[test]
2383    fn to_binary_not_enough_args() {
2384        // DW: dw::util::Coercions::toBinary("some")
2385        let pel = r#"[":apply", "0-46", [":ref", "0-29", "toBinary"], [":str", "30-36", "some"]]"#;
2386
2387        let expression = Parser::new().parse_str(pel).unwrap();
2388        let result = Runtime::new().eval(&expression).unwrap_err();
2389
2390        assert_eq!(result.kind, RuntimeErrorKind::NotEnoughArguments);
2391    }
2392
2393    #[test]
2394    fn to_binary_to_many_args() {
2395        // DW: dw::util::Coercions::toBinary("some", "UTF-8", "UTF-8")
2396        let pel = r#"[":apply", "0-46", [":ref", "0-29", "toBinary"], [":str", "30-36", "some"], [":str", "38-45", "UTF-8"], [":str", "38-45", "UTF-8"]]"#;
2397
2398        let expression = Parser::new().parse_str(pel).unwrap();
2399        let result = Runtime::new().eval(&expression).unwrap_err();
2400
2401        assert_eq!(result.kind, RuntimeErrorKind::TooManyArguments);
2402    }
2403
2404    #[test]
2405    fn to_binary_invalid_argument() {
2406        // DW: dw::util::Coercions::toBinary("some", "UTF-32")
2407        let pel = r#"[":apply", "0-46", [":ref", "0-29", "toBinary"], [":str", "30-36", "some"], [":str", "38-45", "UTF-32"]]"#;
2408
2409        let expression = Parser::new().parse_str(pel).unwrap();
2410        let result = Runtime::new().eval(&expression).unwrap_err();
2411
2412        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
2413    }
2414
2415    #[test]
2416    fn from_binary() {
2417        // DW: dw::util::Coercions::toString(dw::util::Coercions::toBinary("some", "UTF-8"), "UTF-8")
2418        let pel = r#"[":apply", "0-86", [":ref", "0-29", "toString"], [":apply", "30-76", [":ref", "30-59", "toBinary"], [":str", "60-66", "some"], [":str", "68-75", "UTF-8"]], [":str", "78-85", "UTF-8"]]"#;
2419
2420        let expression = Parser::new().parse_str(pel).unwrap();
2421        let result = Runtime::new()
2422            .eval(&expression)
2423            .unwrap()
2424            .complete()
2425            .unwrap();
2426
2427        assert_eq!(result.as_str(), Some("some"));
2428    }
2429
2430    #[test]
2431    fn from_binary_not_enough_args() {
2432        // DW: dw::util::Coercions::toString(dw::util::Coercions::toBinary("some", "UTF-8"), "UTF-8")
2433        let pel = r#"[":apply", "0-86", [":ref", "0-29", "toString"], [":apply", "30-76", [":ref", "30-59", "toBinary"], [":str", "60-66", "some"], [":str", "68-75", "UTF-8"]]]"#;
2434
2435        let expression = Parser::new().parse_str(pel).unwrap();
2436        let result = Runtime::new().eval(&expression).unwrap_err();
2437
2438        assert_eq!(result.kind, RuntimeErrorKind::NotEnoughArguments);
2439    }
2440
2441    #[test]
2442    fn from_binary_to_many_args() {
2443        // DW: dw::util::Coercions::toString(dw::util::Coercions::toBinary("some", "UTF-8"), "UTF-8", "UTF-8")
2444        let pel = r#"[":apply", "0-86", [":ref", "0-29", "toString"], [":apply", "30-76", [":ref", "30-59", "toBinary"], [":str", "60-66", "some"], [":str", "68-75", "UTF-8"]], [":str", "78-85", "UTF-8"], [":str", "78-85", "UTF-8"]]"#;
2445
2446        let expression = Parser::new().parse_str(pel).unwrap();
2447        let result = Runtime::new().eval(&expression).unwrap_err();
2448
2449        assert_eq!(result.kind, RuntimeErrorKind::TooManyArguments);
2450    }
2451
2452    #[test]
2453    fn from_binary_invalid_argument() {
2454        // DW: dw::util::Coercions::toString(dw::util::Coercions::toBinary("some", "UTF-8"), "UTF-32")
2455        let pel = r#"[":apply", "0-86", [":ref", "0-29", "toString"], [":apply", "30-76", [":ref", "30-59", "toBinary"], [":str", "60-66", "some"], [":str", "68-75", "UTF-8"]], [":str", "78-85", "UTF-32"]]"#;
2456
2457        let expression = Parser::new().parse_str(pel).unwrap();
2458        let result = Runtime::new().eval(&expression).unwrap_err();
2459
2460        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
2461    }
2462
2463    #[test]
2464    fn from_base64() {
2465        // DW: dw::core::Binaries::fromBase64("c29tZQ==")
2466        let pel =
2467            r#"[":apply", "0-46", [":ref", "0-29", "fromBase64"], [":str", "30-36", "c29tZQ=="]]"#;
2468
2469        let expression = Parser::new().parse_str(pel).unwrap();
2470        let result = Runtime::new()
2471            .eval(&expression)
2472            .unwrap()
2473            .complete()
2474            .unwrap();
2475
2476        assert_eq!(result.as_binary().unwrap(), "some".as_bytes());
2477    }
2478
2479    #[test]
2480    fn from_base64_not_enough_args() {
2481        // DW: dw::core::Binaries::fromBase64()
2482        let pel = r#"[":apply", "0-46", [":ref", "0-29", "fromBase64"]]"#;
2483
2484        let expression = Parser::new().parse_str(pel).unwrap();
2485        let result = Runtime::new().eval(&expression).unwrap_err();
2486
2487        assert_eq!(result.kind, RuntimeErrorKind::NotEnoughArguments);
2488    }
2489
2490    #[test]
2491    fn from_base64_to_many_args() {
2492        // DW: dw::core::Binaries::fromBase64("some", "UTF-8")
2493        let pel = r#"[":apply", "0-46", [":ref", "0-29", "fromBase64"], [":str", "30-36", "AA"], [":str", "38-45", "UTF-8"]]"#;
2494
2495        let expression = Parser::new().parse_str(pel).unwrap();
2496        let result = Runtime::new().eval(&expression).unwrap_err();
2497
2498        assert_eq!(result.kind, RuntimeErrorKind::TooManyArguments);
2499    }
2500
2501    #[test]
2502    fn from_base64_invalid_argument() {
2503        // DW: dw::core::Binaries::fromBase64("some")
2504        let pel = r#"[":apply", "0-46", [":ref", "0-29", "fromBase64"], [":str", "30-36", "0"]]"#;
2505
2506        let expression = Parser::new().parse_str(pel).unwrap();
2507        let result = Runtime::new().eval(&expression).unwrap_err();
2508
2509        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
2510    }
2511
2512    #[test]
2513    fn to_base64() {
2514        // DW: dw::core::Binaries::toBase64(dw::core::Binaries::fromBase64("some", "UTF-8"), "UTF-8")
2515        let pel = r#"[":apply", "0-86", [":ref", "0-29", "toBase64"], [":apply", "30-76", [":ref", "30-59", "fromBase64"], [":str", "60-66", "AAAA"]]]"#;
2516
2517        let expression = Parser::new().parse_str(pel).unwrap();
2518        let result = Runtime::new()
2519            .eval(&expression)
2520            .unwrap()
2521            .complete()
2522            .unwrap();
2523
2524        assert_eq!(result.as_str(), Some("AAAA"));
2525    }
2526
2527    #[test]
2528    fn to_base64_not_enough_args() {
2529        // DW: dw::core::Binaries::toBase64()
2530        let pel = r#"[":apply", "0-86", [":ref", "0-29", "toBase64"]]"#;
2531
2532        let expression = Parser::new().parse_str(pel).unwrap();
2533        let result = Runtime::new().eval(&expression).unwrap_err();
2534
2535        assert_eq!(result.kind, RuntimeErrorKind::NotEnoughArguments);
2536    }
2537
2538    #[test]
2539    fn to_base64_to_many_args() {
2540        // DW: dw::core::Binaries::toBase64(dw::core::Binaries::fromBase64("some"), "UTF-8")
2541        let pel = r#"[":apply", "0-86", [":ref", "0-29", "toBase64"], [":apply", "30-76", [":ref", "30-59", "fromBase64"], [":str", "60-66", "AAAA"]], [":str", "78-85", "UTF-8"]]"#;
2542
2543        let expression = Parser::new().parse_str(pel).unwrap();
2544        let result = Runtime::new().eval(&expression).unwrap_err();
2545
2546        assert_eq!(result.kind, RuntimeErrorKind::TooManyArguments);
2547    }
2548
2549    #[test]
2550    fn decode_basic_auth() {
2551        // dw::util::Coercions::toString(dw::core::Binaries::fromBase64(dw::core::Strings::substringAfter('basic c29tZTpvdGhlcg==', 'basic ')), 'UTF-8')
2552        let pel = r#"[":apply", "0-143", [":ref", "0-29", "toString"], [":apply", "30-133", [":ref", "30-60", "fromBase64"], [":apply", "61-132", [":ref", "61-94", "substringAfter"], [":str", "95-120", "bearer c29tZTpvdGhlcg=="], [":str", "122-131", "bearer "]]], [":str", "135-142", "UTF-8"]]"#;
2553
2554        let expression = Parser::new().parse_str(pel).unwrap();
2555        let result = Runtime::new()
2556            .eval(&expression)
2557            .unwrap()
2558            .complete()
2559            .unwrap();
2560
2561        assert_eq!(result.as_str().unwrap(), "some:other");
2562    }
2563
2564    #[test]
2565    fn decode_basic_auth_auto_coerce() {
2566        // dw::core::Strings::substringAfter(dw::core::Binaries::fromBase64("c29tZTpvdGhlcg=="), ':')
2567        let pel = r#"[":apply", "0-90", [":ref", "0-33", "substringAfter"], [":apply", "34-84", [":ref", "34-64", "fromBase64"], [":str", "65-83", "c29tZTpvdGhlcg=="]], [":str", "86-89", ":"]]"#;
2568
2569        let expression = Parser::new().parse_str(pel).unwrap();
2570        let result = Runtime::new()
2571            .eval(&expression)
2572            .unwrap()
2573            .complete()
2574            .unwrap();
2575
2576        assert_eq!(result.as_str().unwrap(), "other");
2577    }
2578
2579    #[test]
2580    fn encode_basic_auth() {
2581        // dw::core::Binaries::toBase64(dw::util::Coercions::toBinary('some:other', 'utf-8'))
2582        let pel = r#"[":apply", "0-82", [":ref", "0-28", "toBase64"], [":apply", "29-81", [":ref", "29-58", "toBinary"], [":str", "59-71", "some:other"], [":str", "73-80", "utf-8"]]]"#;
2583
2584        let expression = Parser::new().parse_str(pel).unwrap();
2585        let result = Runtime::new()
2586            .eval(&expression)
2587            .unwrap()
2588            .complete()
2589            .unwrap();
2590
2591        assert_eq!(result.as_str().unwrap(), "c29tZTpvdGhlcg==");
2592    }
2593
2594    #[test]
2595    fn encode_basic_auth_auto_coerce() {
2596        // dw::core::Binaries::toBase64('some:other')
2597        let pel =
2598            r#"[":apply", "0-0", [":ref", "0-28", "toBase64"], [":str", "0-0", "some:other"]]"#;
2599
2600        let expression = Parser::new().parse_str(pel).unwrap();
2601        let result = Runtime::new()
2602            .eval(&expression)
2603            .unwrap()
2604            .complete()
2605            .unwrap();
2606
2607        assert_eq!(result.as_str().unwrap(), "c29tZTpvdGhlcg==");
2608    }
2609
2610    #[test]
2611    fn multimap_return_full() {
2612        let origin = "<envelope><some>b</some></envelope>";
2613        let value = DocumentBuilder::root(
2614            QualifiedName::new("", "envelope"),
2615            Rc::new(origin.to_string()),
2616            0..origin.len(),
2617        )
2618        .with_element(QualifiedName::new("", "some"), 10..24)
2619        .with_text("b")
2620        .finish_element()
2621        .finish_element()
2622        .build();
2623
2624        // DW: payload
2625        let pel = r#"[":ref", "0-0", "payload"]"#;
2626
2627        assert_eq!(
2628            evaluate(pel, value).as_doc_node().unwrap().content(),
2629            Some(origin.to_string())
2630        )
2631    }
2632
2633    #[test]
2634    fn multimap_return_obj() {
2635        let origin = "<envelope><some>b</some></envelope>";
2636        let value = DocumentBuilder::root(
2637            QualifiedName::new("", "envelope"),
2638            Rc::new(origin.to_string()),
2639            0..origin.len(),
2640        )
2641        .with_element(QualifiedName::new("", "some"), 10..24)
2642        .with_text("b")
2643        .finish_element()
2644        .finish_element()
2645        .build();
2646
2647        // DW: payload.envelope
2648        let pel = r#"
2649            [".", "0-0",
2650                [":ref", "0-0", "payload"],
2651                [":str", "0-0", "envelope"]
2652            ]
2653        "#;
2654
2655        assert_eq!(
2656            evaluate(pel, value).as_doc_node().unwrap().content(),
2657            Some("<?xml version='1.0' encoding='UTF-8'?>\n<some>\n    b\n</some>".to_string())
2658        )
2659    }
2660
2661    #[test]
2662    fn access_multimap_by_name() {
2663        let origin = "<envelope>b</envelope>";
2664        let value = DocumentBuilder::root(
2665            QualifiedName::new("", "envelope"),
2666            Rc::new(origin.to_string()),
2667            0..origin.len(),
2668        )
2669        .with_text("b")
2670        .finish_element()
2671        .build();
2672
2673        // DW: payload.envelope
2674        let pel = r#"
2675            ["==", "0-0",
2676                [":str", "0-0", "b"],
2677                [".", "0-0",
2678                    [":ref", "0-0", "payload"],
2679                    [":str", "0-0", "envelope"]
2680                ]
2681            ]
2682        "#;
2683
2684        evaluate_and_assert(pel, value, Value::bool(true))
2685    }
2686
2687    #[test]
2688    fn multimap_content() {
2689        let value = DocumentBuilder::root(
2690            QualifiedName::new("", "envelope"),
2691            Rc::new(Default::default()),
2692            0..0,
2693        )
2694        .with_element(QualifiedName::prefixed("s", "some-uri", "some"), 0..0)
2695        .with_attribute(
2696            QualifiedName::prefixed("s", "some-uri", "qualified"),
2697            "qualified",
2698        )
2699        .with_attribute(
2700            QualifiedName::prefixed("", "some-uri", "unqualified"),
2701            "unqualified",
2702        )
2703        .with_element(QualifiedName::prefixed("s", "some-uri", "first"), 0..0)
2704        .finish_element()
2705        .with_element(QualifiedName::prefixed("s", "some-uri", "second"), 0..0)
2706        .with_text("b")
2707        .finish_element()
2708        .finish_element()
2709        .finish_element()
2710        .build();
2711
2712        // DW: payload.envelope
2713        let pel = r#"
2714            [".", "0-0",
2715                [":ref", "0-0", "payload"],
2716                [":str", "0-0", "envelope"]
2717            ]
2718        "#;
2719
2720        assert_eq!(
2721            evaluate(pel, value.clone())
2722                .as_doc_node()
2723                .unwrap()
2724                .content(),
2725            Some(
2726                r#"<?xml version='1.0' encoding='UTF-8'?>
2727<s:some xmlns:s="some-uri" s:qualified="qualified" xmlns="some-uri" unqualified="unqualified">
2728    <s:first/>
2729    <s:second>
2730        b
2731    </s:second>
2732</s:some>"#
2733                    .to_string()
2734            )
2735        );
2736
2737        // DW: payload.envelope.some
2738        let pel = r#"
2739            [".", "0-0",
2740                [".", "0-0",
2741                    [":ref", "0-0", "payload"],
2742                    [":str", "0-0", "envelope"]
2743                ],
2744                [":str", "0-0", "some"]
2745            ]
2746        "#;
2747
2748        assert_eq!(evaluate(pel, value).as_doc_node().unwrap().content(), None)
2749    }
2750
2751    #[test]
2752    fn access_multimap_by_qname() {
2753        // <envelope xmlns="http://test.com">b</envelope>
2754        let value = DocumentBuilder::root(
2755            QualifiedName::new("http://test.com", "envelope"),
2756            Rc::new("".to_string()),
2757            0..0,
2758        )
2759        .with_text("b")
2760        .finish_element()
2761        .build();
2762
2763        // DW: "b" == payload.http://test.com#envelope
2764        let pel = r#"
2765            ["==", "0-0",
2766                [":str", "0-0", "b"],
2767                [".", "0-0", [":ref", "0-0", "payload"], [":name", "0-0", [":ns", "0-0", "http://test.com"], [":str", "0-0", "envelope"]]]
2768            ]
2769        "#;
2770
2771        evaluate_and_assert(pel, value, Value::bool(true))
2772    }
2773
2774    #[test]
2775    fn access_multimap_attribute_by_name() {
2776        // <envelope xmlns="http://test.com" xmlns:a="http://another.com" a:attr="another" attr="JJ">b</envelope>
2777        let value = DocumentBuilder::root(
2778            QualifiedName::new("http://test.com", "envelope"),
2779            Rc::new("".to_string()),
2780            0..0,
2781        )
2782        .with_attribute(QualifiedName::new("http://another.com", "attr"), "another")
2783        .with_attribute(QualifiedName::new("http://test.com", "attr"), "JJ")
2784        .with_text("b")
2785        .finish_element()
2786        .build();
2787
2788        // DW: "b" == payload.http://test.com#envelope
2789        let pel = r#"
2790            ["==", "0-0",
2791                [":str", "0-0", "another"],
2792                ["@", "0-0", [".", "0-0", [":ref", "0-0", "payload"], [":str", "0-0", "envelope"]], [":str", "0-0", "attr"]]
2793            ]
2794        "#;
2795
2796        evaluate_and_assert(pel, value, Value::bool(true))
2797    }
2798
2799    #[test]
2800    fn access_multimap_attribute_by_qname() {
2801        // <envelope xmlns="http://test.com" xmlns:a="http://another.com" a:attr="another" attr="JJ">b</envelope>
2802        let value = DocumentBuilder::root(
2803            QualifiedName::new("http://test.com", "envelope"),
2804            Rc::new("".to_string()),
2805            0..0,
2806        )
2807        .with_attribute(QualifiedName::new("http://another.com", "attr"), "another")
2808        .with_attribute(QualifiedName::new("http://test.com", "attr"), "JJ")
2809        .with_text("b")
2810        .finish_element()
2811        .build();
2812
2813        // DW: "b" == payload.http://test.com#envelope
2814        let pel = r#"
2815            ["==", "0-0",
2816                [":str", "0-0", "JJ"],
2817                ["@", "0-0", [".", "0-0", [":ref", "0-0", "payload"], [":str", "0-0", "envelope"]], [":name", "0-0", [":ns", "0-0", "http://test.com"], [":str", "0-0", "attr"]]]
2818            ]
2819        "#;
2820
2821        evaluate_and_assert(pel, value, Value::bool(true))
2822    }
2823
2824    #[test]
2825    fn access_multimap_by_non_existing_key() {
2826        // <envelope>b</envelope>
2827        let value = DocumentBuilder::root(
2828            QualifiedName::new("", "envelope"),
2829            Rc::new("".to_string()),
2830            0..0,
2831        )
2832        .with_text("b")
2833        .finish_element()
2834        .build();
2835
2836        // DW: payload.envelope == b
2837        let pel = r#"
2838            ["==", "0-0",
2839                [".", "0-0",
2840                    [":ref", "0-0", "payload"],
2841                    [":str", "0-0", "env"]
2842                ],
2843                [":null", "0-0"]
2844            ]
2845        "#;
2846        evaluate_and_assert(pel, value, Value::bool(true))
2847    }
2848
2849    #[test]
2850    fn access_multimap_by_repeated_key() {
2851        // <envelope>
2852        //      <a>a1</a>
2853        //      <a>a2</a>
2854        // </envelope>
2855        let value = DocumentBuilder::root(
2856            QualifiedName::new("", "envelope"),
2857            Rc::new("".to_string()),
2858            0..0,
2859        )
2860        .with_element(QualifiedName::new("", "a"), 0..0)
2861        .with_text("a1")
2862        .finish_element()
2863        .with_element(QualifiedName::new("", "a"), 0..0)
2864        .with_text("a2")
2865        .finish_element()
2866        .finish_element()
2867        .build();
2868
2869        // DW: payload.envelope.a == b
2870        let pel = r#"
2871            ["==", "0-0",
2872                [".", "0-0",
2873                    [".", "0-0",
2874                        [":ref", "0-0", "payload"],
2875                        [":str", "0-0", "envelope"]
2876                    ],
2877                    [":str", "0-0", "a"]
2878                ],
2879                [":str", "0-0", "a1"]
2880            ]
2881        "#;
2882        evaluate_and_assert(pel, value, Value::bool(true))
2883    }
2884
2885    #[test]
2886    fn compare_multimaps() {
2887        // <envelope>
2888        //      <a1>a<b/></a1>
2889        //      <a2>a<b/></a2>
2890        // </envelope>
2891        let value = DocumentBuilder::root(
2892            QualifiedName::new("", "envelope"),
2893            Rc::new("".to_string()),
2894            0..0,
2895        )
2896        .with_element(QualifiedName::new("", "a1"), 0..0)
2897        .with_text("a")
2898        .with_element(QualifiedName::new("", "b"), 0..0)
2899        .finish_element()
2900        .finish_element()
2901        .with_element(QualifiedName::new("", "a2"), 0..0)
2902        .with_text("a")
2903        .with_element(QualifiedName::new("", "b"), 0..0)
2904        .finish_element()
2905        .finish_element()
2906        .finish_element()
2907        .build();
2908
2909        // DW: payload.envelope.a1 == payload.envelope.a2
2910        let pel = r#"
2911            ["==", "0-0",
2912                [".", "0-0",
2913                    [".", "0-0",
2914                        [":ref", "0-0", "payload"],
2915                        [":str", "0-0", "envelope"]
2916                    ],
2917                    [":str", "0-0", "a1"]
2918                ],
2919                [".", "0-0",
2920                    [".", "0-0",
2921                        [":ref", "0-0", "payload"],
2922                        [":str", "0-0", "envelope"]
2923                    ],
2924                    [":str", "0-0", "a2"]
2925                ]
2926            ]
2927        "#;
2928        evaluate_and_assert(pel, value, Value::bool(true))
2929    }
2930
2931    #[test]
2932    fn access_multimap_by_index() {
2933        // <envelope>b</envelope>
2934        let value = DocumentBuilder::root(
2935            QualifiedName::new("", "envelope"),
2936            Rc::new("".to_string()),
2937            0..0,
2938        )
2939        .with_text("b")
2940        .finish_element()
2941        .build();
2942
2943        // DW: payload.envelope == b
2944        let pel = r#"
2945            ["==", "0-0",
2946                [".", "0-0",
2947                    [":ref", "0-0", "payload"],
2948                    [":nbr", "0-0", "0"]
2949                ],
2950                [":str", "0-0", "b"]
2951            ]
2952        "#;
2953        evaluate_and_assert(pel, value, Value::bool(true))
2954    }
2955
2956    #[test]
2957    fn access_multimap_by_non_existing_index() {
2958        // <envelope>b</envelope>
2959        let value = DocumentBuilder::root(
2960            QualifiedName::new("", "envelope"),
2961            Rc::new("".to_string()),
2962            0..0,
2963        )
2964        .with_text("b")
2965        .finish_element()
2966        .build();
2967
2968        // DW: payload.envelope == b
2969        let pel = r#"
2970            ["==", "0-0",
2971                [".", "0-0",
2972                    [":ref", "0-0", "payload"],
2973                    [":nbr", "0-0", "1"]
2974                ],
2975                [":null", "0-0"]
2976            ]
2977        "#;
2978        evaluate_and_assert(pel, value, Value::bool(true))
2979    }
2980
2981    #[test]
2982    fn access_multimap_multivalue_by_name() {
2983        // <envelope xmlns="http://test.com"><b>1</b><b>2</b></envelope>
2984        let value = DocumentBuilder::root(
2985            QualifiedName::new("http://test.com", "envelope"),
2986            Rc::new("".to_string()),
2987            0..0,
2988        )
2989        .with_element(QualifiedName::new("http://test.com", "b"), 0..0)
2990        .with_text("1")
2991        .finish_element()
2992        .with_element(QualifiedName::new("http://test.com", "b"), 0..0)
2993        .with_text("2")
2994        .finish_element()
2995        .finish_element()
2996        .build();
2997
2998        let pel = r#"
2999            ["*", "0-0", [".", "0-0", [":ref", "0-0", "payload"], [":str", "0-0", "envelope"]], [":str", "0-0", "b"]]
3000        "#;
3001
3002        let expected = Value::array(vec![Value::string("1"), Value::string("2")]);
3003        evaluate_and_assert(pel, value, expected)
3004    }
3005
3006    #[test]
3007    fn access_multimap_multivalue_by_qname() {
3008        // <envelope xmlns="http://test.com" xmlns:o="http://other.com"><b>1</b><o:b>2</o:b><b>3</b></envelope>
3009        let value = DocumentBuilder::root(
3010            QualifiedName::new("http://test.com", "envelope"),
3011            Rc::new("".to_string()),
3012            0..0,
3013        )
3014        .with_element(QualifiedName::new("http://test.com", "b"), 0..0)
3015        .with_text("1")
3016        .finish_element()
3017        .with_element(QualifiedName::new("http://other.com", "b"), 0..0)
3018        .with_text("2")
3019        .finish_element()
3020        .with_element(QualifiedName::new("http://test.com", "b"), 0..0)
3021        .with_text("3")
3022        .finish_element()
3023        .finish_element()
3024        .build();
3025
3026        let pel = r#"
3027            ["*", "0-0", [".", "0-0", [":ref", "0-0", "payload"], [":str", "0-0", "envelope"]], [":name", "0-0", [":ns", "0-0", "http://test.com"], [":str", "0-0", "b"]]]
3028        "#;
3029
3030        let expected = Value::array(vec![Value::string("1"), Value::string("3")]);
3031        evaluate_and_assert(pel, value, expected)
3032    }
3033
3034    #[cfg(feature = "experimental_coerced_type")]
3035    #[test]
3036    fn multimap_root_coerced() {
3037        let origin = "<envelope><some>b</some></envelope>";
3038        let value = DocumentBuilder::root(
3039            QualifiedName::new("", "envelope"),
3040            Rc::new(origin.to_string()),
3041            0..origin.len(),
3042        )
3043        .with_element(QualifiedName::new("", "some"), 10..24)
3044        .with_text("b")
3045        .finish_element()
3046        .finish_element()
3047        .build();
3048
3049        // DW: payload ++ "envelope"
3050        let pel = r#"[":apply", "0-0", [":ref", "0-0", "++"], [":ref", "0-0", "payload"], [":str", "0-0", "envelope"]]"#;
3051
3052        assert_eq!(
3053            evaluate(pel, value.clone()).as_str().unwrap(),
3054            origin.to_string() + "envelope"
3055        );
3056
3057        // DW: "envelope" ++ envelope
3058        let pel = r#"[":apply", "0-0", [":ref", "0-0", "++"], [":str", "0-0", "envelope"], [":ref", "0-0", "payload"]]"#;
3059        assert_eq!(
3060            evaluate(pel, value.clone()).as_str().unwrap(),
3061            "envelope".to_string() + origin
3062        );
3063
3064        // DW: payload == "<envelope><some>b</some></envelope>"
3065        let pel = r#"
3066            ["==", "0-0",
3067                [":ref", "0-0", "payload"],
3068                [":str", "0-0", "<envelope><some>b</some></envelope>"]
3069            ]
3070        "#;
3071        assert!(evaluate(pel, value).as_bool().unwrap());
3072    }
3073
3074    #[test]
3075    fn multimap_not_coerced() {
3076        let origin = "<envelope><some>b</some></envelope>";
3077        let value = DocumentBuilder::root(
3078            QualifiedName::new("", "envelope"),
3079            Rc::new(origin.to_string()),
3080            0..origin.len(),
3081        )
3082        .with_element(QualifiedName::new("", "some"), 10..24)
3083        .with_text("b")
3084        .finish_element()
3085        .finish_element()
3086        .build();
3087
3088        // DW: payload.envelope ++ "envelope"
3089        let pel = r#"[":apply", "0-0",
3090            [":ref", "0-0", "++"],
3091            [".", "0-0",
3092                [":ref", "0-0", "payload"],
3093                [":str", "0-0", "envelope"]
3094            ],
3095            [":str", "0-0", "envelope"]
3096        ]"#;
3097
3098        assert_eq!(
3099            &RuntimeErrorKind::TypeMismatch,
3100            evaluate_error(pel, value.clone()).kind(),
3101        );
3102
3103        // DW: payload == "<envelope><some>b</some></envelope>"
3104        let pel = r#"
3105            ["==", "0-0",
3106                [".", "0-0",
3107                   [":ref", "0-0", "payload"],
3108                    [":str", "0-0", "envelope"]
3109                ],
3110                [":str", "0-0", "<some>b</some>"]
3111            ]
3112        "#;
3113        assert!(!evaluate(pel, value).as_bool().unwrap());
3114    }
3115
3116    #[cfg(feature = "experimental_coerced_type")]
3117    #[test]
3118    fn object_root_coerced() {
3119        let origin = r#"{"envelope":{"some":"value"}}"#;
3120        let value = Value::coerced_object(
3121            HashMap::from([(
3122                "envelope".to_string(),
3123                Value::object(HashMap::from([(
3124                    "some".to_string(),
3125                    Value::string("value"),
3126                )])),
3127            )]),
3128            Rc::new(origin.to_string()),
3129        );
3130
3131        // DW: payload ++ "envelope"
3132        let pel = r#"[":apply", "0-0", [":ref", "0-0", "++"], [":ref", "0-0", "payload"], [":str", "0-0", "envelope"]]"#;
3133
3134        assert_eq!(
3135            evaluate(pel, value.clone()).as_str().unwrap(),
3136            origin.to_string() + "envelope"
3137        );
3138
3139        // DW: "envelope" ++ envelope
3140        let pel = r#"[":apply", "0-0", [":ref", "0-0", "++"], [":str", "0-0", "envelope"], [":ref", "0-0", "payload"]]"#;
3141        assert_eq!(
3142            evaluate(pel, value.clone()).as_str().unwrap(),
3143            "envelope".to_string() + origin
3144        );
3145
3146        // DW: payload == "{"envelope":"value"}"
3147        let pel = r#"
3148            ["==", "0-0",
3149                [":ref", "0-0", "payload"],
3150                [":str", "0-0", "{\"envelope\":{\"some\":\"value\"}}"]
3151            ]
3152        "#;
3153        assert!(evaluate(pel, value).as_bool().unwrap());
3154    }
3155
3156    #[test]
3157    fn object_not_coerced() {
3158        #[cfg(feature = "experimental_coerced_type")]
3159        let origin = r#"{"envelope":{"some":"value"}}"#;
3160
3161        let map = HashMap::from([(
3162            "envelope".to_string(),
3163            Value::object(HashMap::from([(
3164                "some".to_string(),
3165                Value::string("value"),
3166            )])),
3167        )]);
3168
3169        #[cfg(feature = "experimental_coerced_type")]
3170        let value = Value::coerced_object(map, Rc::new(origin.to_string()));
3171
3172        #[cfg(not(feature = "experimental_coerced_type"))]
3173        let value = Value::object(map);
3174
3175        // DW: payload.envelope ++ "envelope"
3176        let pel = r#"[":apply", "0-0",
3177            [":ref", "0-0", "++"],
3178            [".", "0-0",
3179                [":ref", "0-0", "payload"],
3180                [":str", "0-0", "envelope"]
3181            ],
3182            [":str", "0-0", "envelope"]
3183        ]"#;
3184
3185        assert_eq!(
3186            &RuntimeErrorKind::TypeMismatch,
3187            evaluate_error(pel, value.clone()).kind(),
3188        );
3189
3190        // DW: payload == "{"some":"value"}"
3191        let pel = r#"
3192            ["==", "0-0",
3193                [".", "0-0",
3194                   [":ref", "0-0", "payload"],
3195                    [":str", "0-0", "envelope"]
3196                ],
3197                [":str", "0-0", "{\"some\":\"value\"}"]
3198            ]
3199        "#;
3200        assert!(!evaluate(pel, value).as_bool().unwrap());
3201    }
3202
3203    fn evaluate_error(pel: &str, payload: Value) -> RuntimeError {
3204        let context: Prelude = HashMap::from([("payload", payload)]);
3205
3206        let expression = Parser::new().parse_str(pel).unwrap();
3207        Runtime::new()
3208            .eval_with_context(&expression, &context)
3209            .err()
3210            .unwrap()
3211    }
3212
3213    fn evaluate(pel: &str, payload: Value) -> Value {
3214        let context: Prelude = HashMap::from([("payload", payload)]);
3215
3216        let expression = Parser::new().parse_str(pel).unwrap();
3217        Runtime::new()
3218            .eval_with_context(&expression, &context)
3219            .unwrap()
3220            .complete()
3221            .unwrap()
3222    }
3223
3224    fn evaluate_and_assert(pel: &str, payload: Value, expected: Value) {
3225        assert_eq!(evaluate(pel, payload), expected)
3226    }
3227
3228    mod partial_evaluation {
3229        use std::collections::HashMap;
3230
3231        use crate::{
3232            expression::Symbol,
3233            parser::Parser,
3234            runtime::{value::Value, Binding, Context, Runtime, ValueHandler},
3235            Reference,
3236        };
3237
3238        struct TestContextChain {
3239            contexts: Vec<HashMap<String, Value>>,
3240        }
3241
3242        impl TestContextChain {
3243            fn new<const N: usize>(entries: [(&str, Value); N]) -> Self {
3244                Self { contexts: vec![] }.then(entries)
3245            }
3246
3247            fn then<const N: usize>(mut self, entries: [(&str, Value); N]) -> Self {
3248                self.contexts
3249                    .push(entries.map(|(k, v)| (k.to_string(), v)).into());
3250                self
3251            }
3252
3253            fn next(mut self) -> Self {
3254                self.contexts.remove(0);
3255                self
3256            }
3257        }
3258
3259        impl Context for TestContextChain {
3260            fn resolve(&self, symbol: &Symbol) -> Binding {
3261                let s = symbol.as_str();
3262                match self.contexts.first().expect("Empty context").get(s) {
3263                    Some(value) => Binding::Available(value.clone()),
3264                    None => {
3265                        if self.contexts.iter().any(|c| c.contains_key(s)) {
3266                            Binding::Pending
3267                        } else {
3268                            Binding::Unknown
3269                        }
3270                    }
3271                }
3272            }
3273
3274            fn value_handler(&self, _reference: Reference) -> Option<&dyn ValueHandler> {
3275                None
3276            }
3277        }
3278
3279        #[test]
3280        fn if_else_pending_condition() {
3281            let runtime = Runtime::new();
3282            let parser = Parser::new();
3283
3284            let context_1 = TestContextChain::new([("a", Value::string("ctx1".to_string()))])
3285                .then([("condition", Value::bool(true))])
3286                .then([("b", Value::string("ctx3".to_string()))]);
3287
3288            // DW: if (condition) a else b
3289            let pel_1 = r#"[":if", "0-23", [":ref", "4-9", "condition"], [":ref", "11-14", "a"], [":ref", "20-23", "b"]]"#;
3290
3291            let expression_1 = parser.parse_str(pel_1).unwrap();
3292            let expression_2 = runtime
3293                .eval_with_context(&expression_1, &context_1)
3294                .unwrap()
3295                .partial()
3296                .unwrap();
3297
3298            // DW: if (condition) "ctx1" else b
3299            let pel_2 = r#"[":if", "0-23", [":ref", "4-9", "condition"], [":str", "11-14", "ctx1"], [":ref", "20-23", "b"]]"#;
3300
3301            assert_eq!(expression_2, parser.parse_str(pel_2).unwrap());
3302
3303            let context_2 = context_1.next();
3304            let result = runtime
3305                .eval_with_context(&expression_2, &context_2)
3306                .unwrap()
3307                .complete()
3308                .unwrap();
3309
3310            assert_eq!(result.as_str().unwrap(), "ctx1");
3311        }
3312
3313        #[test]
3314        fn if_else_pending_true_branch() {
3315            let runtime = Runtime::new();
3316            let parser = Parser::new();
3317
3318            let context_1 = TestContextChain::new([("a", Value::string("ctx1".to_string()))])
3319                .then([("b", Value::string("ctx2".to_string()))]);
3320
3321            // DW: if (false) a else b
3322            let pel_1 = r#"[":if", "0-23", [":bool", "4-9", "true"], [":ref", "11-14", "b"], [":ref", "20-23", "a"]]"#;
3323
3324            let expression_1 = parser.parse_str(pel_1).unwrap();
3325            let expression_2 = runtime
3326                .eval_with_context(&expression_1, &context_1)
3327                .unwrap()
3328                .partial()
3329                .unwrap();
3330
3331            let pel_2 = r#"[":ref", "11-14", "b"]"#;
3332
3333            assert_eq!(expression_2, parser.parse_str(pel_2).unwrap());
3334
3335            let context_2 = context_1.next();
3336            let result = runtime
3337                .eval_with_context(&expression_2, &context_2)
3338                .unwrap()
3339                .complete()
3340                .unwrap();
3341
3342            assert_eq!(result.as_str().unwrap(), "ctx2");
3343        }
3344
3345        #[test]
3346        fn if_else_pending_false_branch() {
3347            let runtime = Runtime::new();
3348            let parser = Parser::new();
3349
3350            let context_1 = TestContextChain::new([("a", Value::string("ctx1".to_string()))])
3351                .then([("b", Value::string("ctx2".to_string()))]);
3352
3353            // DW: if (false) a else b
3354            let pel_1 = r#"[":if", "0-23", [":bool", "4-9", "false"], [":ref", "11-14", "a"], [":ref", "20-23", "b"]]"#;
3355
3356            let expression_1 = parser.parse_str(pel_1).unwrap();
3357            let expression_2 = runtime
3358                .eval_with_context(&expression_1, &context_1)
3359                .unwrap()
3360                .partial()
3361                .unwrap();
3362
3363            let pel_2 = r#"[":ref", "20-23", "b"]"#;
3364
3365            assert_eq!(expression_2, parser.parse_str(pel_2).unwrap());
3366
3367            let context_2 = context_1.next();
3368            let result = runtime
3369                .eval_with_context(&expression_2, &context_2)
3370                .unwrap()
3371                .complete()
3372                .unwrap();
3373
3374            assert_eq!(result.as_str().unwrap(), "ctx2");
3375        }
3376
3377        #[test]
3378        fn default_left_unavailable() {
3379            let runtime = Runtime::new();
3380            let parser = Parser::new();
3381
3382            let context_1 = TestContextChain::new([("right", Value::string("ctx1".to_string()))])
3383                .then([("left", Value::null())]);
3384
3385            // DW: left default right
3386            let pel_1 = r#"
3387                [":default", "0-6",
3388                    [":ref", "0-1", "left"],
3389                    [":ref", "5-6", "right"]
3390                ]
3391            "#;
3392
3393            let expression_1 = parser.parse_str(pel_1).unwrap();
3394            let expression_2 = runtime
3395                .eval_with_context(&expression_1, &context_1)
3396                .unwrap()
3397                .partial()
3398                .unwrap();
3399
3400            // DW: left default "ctx1"
3401            let pel_2 = r#"
3402                [":default", "0-6",
3403                    [":ref", "0-1", "left"],
3404                    [":str", "5-6", "ctx1"]
3405                ]
3406            "#;
3407
3408            assert_eq!(expression_2, parser.parse_str(pel_2).unwrap());
3409
3410            let context_2 = context_1.next();
3411            let result = runtime
3412                .eval_with_context(&expression_2, &context_2)
3413                .unwrap()
3414                .complete()
3415                .unwrap();
3416
3417            assert_eq!(result.as_str().unwrap(), "ctx1");
3418        }
3419
3420        #[test]
3421        fn default_right_unavailable() {
3422            let runtime = Runtime::new();
3423            let parser = Parser::new();
3424
3425            let context_1 = TestContextChain::new([("left", Value::null())])
3426                .then([("right", Value::string("ctx1".to_string()))]);
3427
3428            // DW: left default right
3429            let pel_1 = r#"
3430                [":default", "0-6",
3431                    [":ref", "0-1", "left"],
3432                    [":ref", "5-6", "right"]
3433                ]
3434            "#;
3435
3436            let expression_1 = parser.parse_str(pel_1).unwrap();
3437            let expression_2 = runtime
3438                .eval_with_context(&expression_1, &context_1)
3439                .unwrap()
3440                .partial()
3441                .unwrap();
3442
3443            // DW: right
3444            let pel_2 = r#"
3445                [":ref", "5-6", "right"]
3446            "#;
3447
3448            assert_eq!(expression_2, parser.parse_str(pel_2).unwrap());
3449
3450            let context_2 = context_1.next();
3451            let result = runtime
3452                .eval_with_context(&expression_2, &context_2)
3453                .unwrap()
3454                .complete()
3455                .unwrap();
3456
3457            assert_eq!(result.as_str().unwrap(), "ctx1");
3458        }
3459    }
3460}