pdk_pel/runtime/
mod.rs

1// Copyright (c) 2025, 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]
1425    fn split_by_string_string() {
1426        // DW: splitBy("Peregrine Expression Language", "re")
1427        let pel = r#"
1428            [":apply", "5-7", 
1429                [":ref", "5-7", "splitBy"], 
1430                [":str", "0-4", "Peregrine Expression Language"], 
1431                [":str", "8-12", "re"]
1432            ]
1433        "#;
1434
1435        let expression = Parser::new().parse_str(pel).unwrap();
1436        let result = Runtime::new()
1437            .eval(&expression)
1438            .unwrap()
1439            .complete()
1440            .unwrap();
1441
1442        let expected = [
1443            Value::string("Pe".to_string()),
1444            Value::string("grine Exp".to_string()),
1445            Value::string("ssion Language".to_string()),
1446        ];
1447
1448        assert_eq!(&expected, result.as_slice().unwrap());
1449    }
1450
1451    #[test]
1452    fn split_by_string_empty_string() {
1453        // DW: splitBy("2022", "")
1454        let pel = r#"
1455            [":apply", "5-7", 
1456                [":ref", "5-7", "splitBy"], 
1457                [":str", "0-4", "2022"], 
1458                [":str", "8-12", ""]
1459            ]
1460        "#;
1461
1462        let expression = Parser::new().parse_str(pel).unwrap();
1463        let result = Runtime::new()
1464            .eval(&expression)
1465            .unwrap()
1466            .complete()
1467            .unwrap();
1468
1469        let expected = [
1470            Value::string("2".to_string()),
1471            Value::string("0".to_string()),
1472            Value::string("2".to_string()),
1473            Value::string("2".to_string()),
1474        ];
1475
1476        assert_eq!(&expected, result.as_slice().unwrap());
1477    }
1478
1479    #[ignore = "bug W-11098754"]
1480    #[test]
1481    fn split_by_string_string_2() {
1482        // DW: splitBy("2022", "2")
1483        let pel = r#"
1484            [":apply", "5-7", 
1485                [":ref", "5-7", "splitBy"], 
1486                [":str", "0-4", "2022"], 
1487                [":str", "8-12", "2"]
1488            ]
1489        "#;
1490
1491        let expression = Parser::new().parse_str(pel).unwrap();
1492        let result = Runtime::new()
1493            .eval(&expression)
1494            .unwrap()
1495            .complete()
1496            .unwrap();
1497
1498        let expected = [
1499            Value::string("".to_string()),
1500            Value::string("0".to_string()),
1501        ];
1502
1503        assert_eq!(&expected, result.as_slice().unwrap());
1504    }
1505
1506    #[test]
1507    fn split_by_number_string() {
1508        // DW: splitBy(1946.03, ".")
1509        let pel = r#"
1510            [":apply", "5-7", 
1511                [":ref", "5-7", "splitBy"], 
1512                [":nbr", "0-4", "1946.03"], 
1513                [":str", "8-12", "."]
1514            ]
1515        "#;
1516
1517        let expression = Parser::new().parse_str(pel).unwrap();
1518        let result = Runtime::new()
1519            .eval(&expression)
1520            .unwrap()
1521            .complete()
1522            .unwrap();
1523
1524        let expected = [
1525            Value::string("1946".to_string()),
1526            Value::string("03".to_string()),
1527        ];
1528
1529        assert_eq!(&expected, result.as_slice().unwrap());
1530    }
1531
1532    #[test]
1533    fn split_by_boolean_string() {
1534        // DW: splitBy(true, "r")
1535        let pel = r#"
1536            [":apply", "5-7", 
1537                [":ref", "5-7", "splitBy"], 
1538                [":bool", "0-4", "true"], 
1539                [":str", "8-12", "r"]
1540            ]
1541        "#;
1542
1543        let expression = Parser::new().parse_str(pel).unwrap();
1544        let result = Runtime::new()
1545            .eval(&expression)
1546            .unwrap()
1547            .complete()
1548            .unwrap();
1549
1550        let expected = [
1551            Value::string("t".to_string()),
1552            Value::string("ue".to_string()),
1553        ];
1554
1555        assert_eq!(&expected, result.as_slice().unwrap());
1556    }
1557
1558    #[test]
1559    fn split_by_null_string() {
1560        // DW: splitBy(null, "re")
1561        let pel = r#"
1562            [":apply", "5-7", 
1563                [":ref", "5-7", "splitBy"], 
1564                [":null", "0-41"], 
1565                [":str", "8-12", "r"]
1566            ]
1567        "#;
1568
1569        let expression = Parser::new().parse_str(pel).unwrap();
1570        let result = Runtime::new()
1571            .eval(&expression)
1572            .unwrap()
1573            .complete()
1574            .unwrap();
1575
1576        assert!(result.is_null());
1577    }
1578
1579    #[test]
1580    fn split_by_string_null_faiil() {
1581        // DW: splitBy("Peregrine", null)
1582        let pel = r#"
1583            [":apply", "5-7", 
1584                [":ref", "5-7", "splitBy"], 
1585                [":str", "0-41", "Peregrine"], 
1586                [":null", "8-12"]
1587            ]
1588        "#;
1589
1590        let expression = Parser::new().parse_str(pel).unwrap();
1591        let error = Runtime::new().eval(&expression).unwrap_err();
1592
1593        assert_eq!(error.kind, RuntimeErrorKind::TypeMismatch);
1594    }
1595
1596    #[test]
1597    fn split_by_array_fail() {
1598        // DW: splitBy(["accept", "accept-encoding", "user-agent"], "re")
1599        let pel = r#"
1600            [":apply", "5-7", 
1601                [":ref", "5-7", "splitBy"], 
1602                [":array", "0-41", 
1603                    [":str", "1-9", "accept"], 
1604                    [":str", "10-27", "accept-encoding"], 
1605                    [":str", "28-40", "user-agent"]
1606                ], 
1607                [":str", "8-12", "r"]
1608            ]
1609        "#;
1610
1611        let expression = Parser::new().parse_str(pel).unwrap();
1612        let error = Runtime::new().eval(&expression).unwrap_err();
1613
1614        assert_eq!(error.kind, RuntimeErrorKind::TypeMismatch);
1615    }
1616
1617    #[test]
1618    fn substring_after_null_string() {
1619        // DW: substringAfter(null, "peregrine")
1620        let pel = r#"
1621            [":apply", "5-7", 
1622                [":ref", "5-7", "substringAfter"], 
1623                [":null", "0-4"], 
1624                [":str", "8-12", "peregrine"]
1625            ]"#;
1626
1627        let expression = Parser::new().parse_str(pel).unwrap();
1628        let result = Runtime::new()
1629            .eval(&expression)
1630            .unwrap()
1631            .complete()
1632            .unwrap();
1633
1634        assert!(result.is_null());
1635    }
1636
1637    #[test]
1638    fn substring_after_string_string() {
1639        // DW: substringAfter("peregrine", "gr")
1640        let pel = r#"
1641            [":apply", "5-7", 
1642                [":ref", "5-7", "substringAfter"], 
1643                [":str", "0-4", "peregrine"], 
1644                [":str", "8-12", "gr"]
1645            ]"#;
1646
1647        let expression = Parser::new().parse_str(pel).unwrap();
1648        let result = Runtime::new()
1649            .eval(&expression)
1650            .unwrap()
1651            .complete()
1652            .unwrap();
1653
1654        assert_eq!(result.as_str().unwrap(), "ine");
1655    }
1656
1657    #[test]
1658    fn substring_after_string_null_fail() {
1659        // DW: substringAfter("peregrine", null)
1660        let pel = r#"
1661            [":apply", "5-7", 
1662                [":ref", "5-7", "substringAfter"], 
1663                [":str", "0-4", "peregrine"], 
1664                [":null", "8-12"]
1665            ]"#;
1666
1667        let expression = Parser::new().parse_str(pel).unwrap();
1668        let error = Runtime::new().eval(&expression).unwrap_err();
1669
1670        assert_eq!(error.kind, RuntimeErrorKind::TypeMismatch);
1671    }
1672
1673    #[test]
1674    fn substring_after_boolean_string() {
1675        // DW: substringAfter(true, "r")
1676        let pel = r#"
1677            [":apply", "5-7", 
1678                [":ref", "5-7", "substringAfter"], 
1679                [":bool", "0-4", "true"], 
1680                [":str", "8-12", "r"]
1681            ]"#;
1682
1683        let expression = Parser::new().parse_str(pel).unwrap();
1684        let result = Runtime::new()
1685            .eval(&expression)
1686            .unwrap()
1687            .complete()
1688            .unwrap();
1689
1690        assert_eq!(result.as_str().unwrap(), "ue");
1691    }
1692
1693    #[test]
1694    fn substring_after_number_number() {
1695        // DW: substringAfter(1234.567, 4.5)
1696        let pel = r#"
1697            [":apply", "5-7", 
1698                [":ref", "5-7", "substringAfter"], 
1699                [":nbr", "0-4", "1234.567"], 
1700                [":nbr", "8-12", "4.5"]
1701            ]"#;
1702
1703        let expression = Parser::new().parse_str(pel).unwrap();
1704        let result = Runtime::new()
1705            .eval(&expression)
1706            .unwrap()
1707            .complete()
1708            .unwrap();
1709
1710        assert_eq!(result.as_str().unwrap(), "67");
1711    }
1712
1713    #[test]
1714    fn substring_after_array_fail() {
1715        // DW: substringAfter(["accept", "accept-encoding", "user-agent"], "re")
1716        let pel = r#"
1717            [":apply", "5-7", 
1718                [":ref", "5-7", "substringAfter"], 
1719                [":array", "0-41", 
1720                    [":str", "1-9", "accept"], 
1721                    [":str", "10-27", "accept-encoding"], 
1722                    [":str", "28-40", "user-agent"]
1723                ],
1724                [":str", "1-10", "re"]
1725            ]
1726        "#;
1727
1728        let expression = Parser::new().parse_str(pel).unwrap();
1729        let result = Runtime::new().eval(&expression).unwrap_err();
1730
1731        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
1732    }
1733
1734    #[test]
1735    fn substring_after_not_found() {
1736        // DW: substringAfter("peregrine", "XX")
1737        let pel = r#"
1738            [":apply", "5-7",
1739                [":ref", "5-7", "substringAfter"],
1740                [":str", "0-4", "peregrine"],
1741                [":str", "8-12", "XX"]
1742            ]"#;
1743
1744        let expression = Parser::new().parse_str(pel).unwrap();
1745        let result = Runtime::new()
1746            .eval(&expression)
1747            .unwrap()
1748            .complete()
1749            .unwrap();
1750
1751        assert_eq!(result.as_str().unwrap(), "");
1752    }
1753
1754    #[test]
1755    fn substring_after_empty_string() {
1756        // DW: substringAfter("peregrine", "")
1757        let pel = r#"
1758            [":apply", "5-7",
1759                [":ref", "5-7", "substringAfter"],
1760                [":str", "0-4", "peregrine"],
1761                [":str", "8-10", ""]
1762            ]"#;
1763
1764        let expression = Parser::new().parse_str(pel).unwrap();
1765        let result = Runtime::new()
1766            .eval(&expression)
1767            .unwrap()
1768            .complete()
1769            .unwrap();
1770
1771        assert_eq!(result.as_str().unwrap(), "peregrine");
1772    }
1773
1774    #[test]
1775    fn substring_after_last_null_string() {
1776        // DW: substringAfterLast(null, "peregrine")
1777        let pel = r#"
1778            [":apply", "5-7", 
1779                [":ref", "5-7", "substringAfterLast"], 
1780                [":null", "0-4"], 
1781                [":str", "8-12", "peregrine"]
1782            ]"#;
1783
1784        let expression = Parser::new().parse_str(pel).unwrap();
1785        let result = Runtime::new()
1786            .eval(&expression)
1787            .unwrap()
1788            .complete()
1789            .unwrap();
1790
1791        assert!(result.is_null());
1792    }
1793
1794    #[test]
1795    fn substring_after_last_string_string() {
1796        // DW: substringAfterLast("Peregrine Expression Language", "re")
1797        let pel = r#"
1798            [":apply", "5-7", 
1799                [":ref", "5-7", "substringAfterLast"], 
1800                [":str", "0-4", "Peregrine Expression Language"], 
1801                [":str", "8-12", "re"]
1802            ]"#;
1803
1804        let expression = Parser::new().parse_str(pel).unwrap();
1805        let result = Runtime::new()
1806            .eval(&expression)
1807            .unwrap()
1808            .complete()
1809            .unwrap();
1810
1811        assert_eq!(result.as_str().unwrap(), "ssion Language");
1812    }
1813
1814    #[test]
1815    fn substring_after_last_string_null_fail() {
1816        // DW: substringAfterLast("peregrine", null)
1817        let pel = r#"
1818            [":apply", "5-7", 
1819                [":ref", "5-7", "substringAfterLast"], 
1820                [":str", "0-4", "peregrine"], 
1821                [":null", "8-12"]
1822            ]"#;
1823
1824        let expression = Parser::new().parse_str(pel).unwrap();
1825        let error = Runtime::new().eval(&expression).unwrap_err();
1826
1827        assert_eq!(error.kind, RuntimeErrorKind::TypeMismatch);
1828    }
1829
1830    #[test]
1831    fn substring_after_last_boolean_string() {
1832        // DW: substringAfterLast(true, "r")
1833        let pel = r#"
1834            [":apply", "5-7", 
1835                [":ref", "5-7", "substringAfterLast"], 
1836                [":bool", "0-4", "true"], 
1837                [":str", "8-12", "r"]
1838            ]"#;
1839
1840        let expression = Parser::new().parse_str(pel).unwrap();
1841        let result = Runtime::new()
1842            .eval(&expression)
1843            .unwrap()
1844            .complete()
1845            .unwrap();
1846
1847        assert_eq!(result.as_str().unwrap(), "ue");
1848    }
1849
1850    #[test]
1851    fn substring_after_last_number_number() {
1852        // DW: substringAfterLast(12123512.3512, 35)
1853        let pel = r#"
1854            [":apply", "5-7", 
1855                [":ref", "5-7", "substringAfterLast"], 
1856                [":nbr", "0-4", "12123512.3512"], 
1857                [":nbr", "8-12", "35"]
1858            ]"#;
1859
1860        let expression = Parser::new().parse_str(pel).unwrap();
1861        let result = Runtime::new()
1862            .eval(&expression)
1863            .unwrap()
1864            .complete()
1865            .unwrap();
1866
1867        assert_eq!(result.as_str().unwrap(), "12");
1868    }
1869
1870    #[test]
1871    fn substring_after_last_array_fail() {
1872        // DW: substringAfterLast(["accept", "accept-encoding", "user-agent"], "re")
1873        let pel = r#"
1874            [":apply", "5-7", 
1875                [":ref", "5-7", "substringAfterLast"], 
1876                [":array", "0-41", 
1877                    [":str", "1-9", "accept"], 
1878                    [":str", "10-27", "accept-encoding"], 
1879                    [":str", "28-40", "user-agent"]
1880                ],
1881                [":str", "1-10", "re"]
1882            ]
1883        "#;
1884
1885        let expression = Parser::new().parse_str(pel).unwrap();
1886        let result = Runtime::new().eval(&expression).unwrap_err();
1887
1888        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
1889    }
1890
1891    #[test]
1892    fn substring_after_last_not_found() {
1893        // DW: substringAfter("peregrine", "XX")
1894        let pel = r#"
1895            [":apply", "5-7",
1896                [":ref", "5-7", "substringAfterLast"],
1897                [":str", "0-4", "peregrine"],
1898                [":str", "8-12", "XX"]
1899            ]"#;
1900
1901        let expression = Parser::new().parse_str(pel).unwrap();
1902        let result = Runtime::new()
1903            .eval(&expression)
1904            .unwrap()
1905            .complete()
1906            .unwrap();
1907
1908        assert_eq!(result.as_str().unwrap(), "");
1909    }
1910
1911    #[test]
1912    fn substring_after_last_empty_string() {
1913        // DW: substringAfter("peregrine", "")
1914        let pel = r#"
1915            [":apply", "5-7",
1916                [":ref", "5-7", "substringAfterLast"],
1917                [":str", "0-4", "peregrine"],
1918                [":str", "8-10", ""]
1919            ]"#;
1920
1921        let expression = Parser::new().parse_str(pel).unwrap();
1922        let result = Runtime::new()
1923            .eval(&expression)
1924            .unwrap()
1925            .complete()
1926            .unwrap();
1927
1928        assert_eq!(result.as_str().unwrap(), "");
1929    }
1930
1931    #[test]
1932    fn substring_before_null_string() {
1933        // DW: substringBefore(null, "peregrine")
1934        let pel = r#"
1935            [":apply", "5-7", 
1936                [":ref", "5-7", "substringBefore"], 
1937                [":null", "0-4"], 
1938                [":str", "8-12", "peregrine"]
1939            ]"#;
1940
1941        let expression = Parser::new().parse_str(pel).unwrap();
1942        let result = Runtime::new()
1943            .eval(&expression)
1944            .unwrap()
1945            .complete()
1946            .unwrap();
1947
1948        assert!(result.is_null());
1949    }
1950
1951    #[test]
1952    fn substring_before_string_string() {
1953        // DW: substringBefore("peregrine", "gr")
1954        let pel = r#"
1955            [":apply", "5-7", 
1956                [":ref", "5-7", "substringBefore"], 
1957                [":str", "0-4", "peregrine"], 
1958                [":str", "8-12", "gr"]
1959            ]"#;
1960
1961        let expression = Parser::new().parse_str(pel).unwrap();
1962        let result = Runtime::new()
1963            .eval(&expression)
1964            .unwrap()
1965            .complete()
1966            .unwrap();
1967
1968        assert_eq!(result.as_str().unwrap(), "pere");
1969    }
1970
1971    #[test]
1972    fn substring_before_string_null_fail() {
1973        // DW: substringBefore("peregrine", null)
1974        let pel = r#"
1975            [":apply", "5-7", 
1976                [":ref", "5-7", "substringBefore"], 
1977                [":str", "0-4", "peregrine"], 
1978                [":null", "8-12"]
1979            ]"#;
1980
1981        let expression = Parser::new().parse_str(pel).unwrap();
1982        let result = Runtime::new().eval(&expression).unwrap_err();
1983
1984        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
1985    }
1986
1987    #[test]
1988    fn substring_before_boolean_string() {
1989        // DW: substringBefore(true, "ue")
1990        let pel = r#"
1991            [":apply", "5-7", 
1992                [":ref", "5-7", "substringBefore"], 
1993                [":bool", "0-4", "true"], 
1994                [":str", "8-12", "ue"]
1995            ]"#;
1996
1997        let expression = Parser::new().parse_str(pel).unwrap();
1998        let result = Runtime::new()
1999            .eval(&expression)
2000            .unwrap()
2001            .complete()
2002            .unwrap();
2003
2004        assert_eq!(result.as_str().unwrap(), "tr");
2005    }
2006
2007    #[test]
2008    fn substring_before_number_number() {
2009        // DW: substringBefore(1234.56, 4.5)
2010        let pel = r#"
2011            [":apply", "5-7", 
2012                [":ref", "5-7", "substringBefore"], 
2013                [":nbr", "0-4", "1234.56"], 
2014                [":nbr", "8-12", "4.5"]
2015            ]"#;
2016
2017        let expression = Parser::new().parse_str(pel).unwrap();
2018        let result = Runtime::new()
2019            .eval(&expression)
2020            .unwrap()
2021            .complete()
2022            .unwrap();
2023
2024        assert_eq!(result.as_str().unwrap(), "123");
2025    }
2026
2027    #[test]
2028    fn substring_before_array_fail() {
2029        // DW: substringBefore(["accept", "accept-encoding", "user-agent"], "re")
2030        let pel = r#"
2031            [":apply", "5-7", 
2032                [":ref", "5-7", "substringBefore"], 
2033                [":array", "0-41", 
2034                    [":str", "1-9", "accept"], 
2035                    [":str", "10-27", "accept-encoding"], 
2036                    [":str", "28-40", "user-agent"]
2037                ],
2038                [":str", "1-10", "re"]
2039            ]
2040        "#;
2041
2042        let expression = Parser::new().parse_str(pel).unwrap();
2043        let result = Runtime::new().eval(&expression).unwrap_err();
2044
2045        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
2046    }
2047
2048    #[test]
2049    fn substring_before_not_found() {
2050        // DW: substringBefore("peregrine", "XX")
2051        let pel = r#"
2052            [":apply", "5-7",
2053                [":ref", "5-7", "substringBefore"],
2054                [":str", "0-4", "peregrine"],
2055                [":str", "8-12", "XX"]
2056            ]"#;
2057
2058        let expression = Parser::new().parse_str(pel).unwrap();
2059        let result = Runtime::new()
2060            .eval(&expression)
2061            .unwrap()
2062            .complete()
2063            .unwrap();
2064
2065        assert_eq!(result.as_str().unwrap(), "");
2066    }
2067
2068    #[test]
2069    fn substring_before_empty_string() {
2070        // DW: substringBefore("peregrine", "")
2071        let pel = r#"
2072            [":apply", "0-55",
2073                [":ref", "0-38", "substringBefore"],
2074                [":str", "39-50", "peregrine"],
2075                [":str", "52-54", ""]
2076            ]"#;
2077
2078        let expression = Parser::new().parse_str(pel).unwrap();
2079        let result = Runtime::new()
2080            .eval(&expression)
2081            .unwrap()
2082            .complete()
2083            .unwrap();
2084
2085        assert_eq!(result.as_str().unwrap(), "");
2086    }
2087
2088    #[test]
2089    fn substring_before_last_null_string() {
2090        // DW: substringBeforeLast(null, "peregrine")
2091        let pel = r#"
2092            [":apply", "5-7", 
2093                [":ref", "5-7", "substringBeforeLast"], 
2094                [":null", "0-4"], 
2095                [":str", "8-12", "peregrine"]
2096            ]"#;
2097
2098        let expression = Parser::new().parse_str(pel).unwrap();
2099        let result = Runtime::new()
2100            .eval(&expression)
2101            .unwrap()
2102            .complete()
2103            .unwrap();
2104
2105        assert!(result.is_null());
2106    }
2107
2108    #[test]
2109    fn substring_before_last_string_string() {
2110        // DW: substringBeforeLast("Peregrine Expression Language", "re")
2111        let pel = r#"
2112            [":apply", "5-7", 
2113                [":ref", "5-7", "substringBeforeLast"], 
2114                [":str", "0-4", "Peregrine Expression Language"], 
2115                [":str", "8-12", "re"]
2116            ]"#;
2117
2118        let expression = Parser::new().parse_str(pel).unwrap();
2119        let result = Runtime::new()
2120            .eval(&expression)
2121            .unwrap()
2122            .complete()
2123            .unwrap();
2124
2125        assert_eq!(result.as_str().unwrap(), "Peregrine Exp");
2126    }
2127
2128    #[test]
2129    fn substring_before_last_string_null_fail() {
2130        // DW: substringBeforeLast("peregrine", null)
2131        let pel = r#"
2132            [":apply", "5-7", 
2133                [":ref", "5-7", "substringBeforeLast"], 
2134                [":str", "0-4", "peregrine"], 
2135                [":null", "8-12"]
2136            ]"#;
2137
2138        let expression = Parser::new().parse_str(pel).unwrap();
2139        let result = Runtime::new().eval(&expression).unwrap_err();
2140
2141        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
2142    }
2143
2144    #[test]
2145    fn substring_before_last_boolean_string() {
2146        // DW: substringBeforeLast(true, "ue")
2147        let pel = r#"
2148            [":apply", "5-7", 
2149                [":ref", "5-7", "substringBeforeLast"], 
2150                [":bool", "0-4", "true"], 
2151                [":str", "8-12", "ue"]
2152            ]"#;
2153
2154        let expression = Parser::new().parse_str(pel).unwrap();
2155        let result = Runtime::new()
2156            .eval(&expression)
2157            .unwrap()
2158            .complete()
2159            .unwrap();
2160
2161        assert_eq!(result.as_str().unwrap(), "tr");
2162    }
2163
2164    #[test]
2165    fn substring_before_last_number_number() {
2166        // DW: substringBeforeLast(121235.123512, 4.5)
2167        let pel = r#"
2168            [":apply", "5-7", 
2169                [":ref", "5-7", "substringBeforeLast"], 
2170                [":nbr", "0-4", "121235.123512"], 
2171                [":nbr", "8-12", "12"]
2172            ]"#;
2173
2174        let expression = Parser::new().parse_str(pel).unwrap();
2175        let result = Runtime::new()
2176            .eval(&expression)
2177            .unwrap()
2178            .complete()
2179            .unwrap();
2180
2181        assert_eq!(result.as_str().unwrap(), "121235.1235");
2182    }
2183
2184    #[test]
2185    fn substring_before_last_array_fail() {
2186        // DW: substringBeforeLast(["accept", "accept-encoding", "user-agent"], "re")
2187        let pel = r#"
2188            [":apply", "5-7", 
2189                [":ref", "5-7", "substringBeforeLast"], 
2190                [":array", "0-41", 
2191                    [":str", "1-9", "accept"], 
2192                    [":str", "10-27", "accept-encoding"], 
2193                    [":str", "28-40", "user-agent"]
2194                ],
2195                [":str", "1-10", "re"]
2196            ]
2197        "#;
2198
2199        let expression = Parser::new().parse_str(pel).unwrap();
2200        let result = Runtime::new().eval(&expression).unwrap_err();
2201
2202        assert_eq!(result.kind, RuntimeErrorKind::TypeMismatch);
2203    }
2204
2205    #[test]
2206    fn substring_before_last_not_found() {
2207        // DW: substringBeforeLast("peregrine", "XX")
2208        let pel = r#"
2209            [":apply", "5-7",
2210                [":ref", "5-7", "substringBeforeLast"],
2211                [":str", "0-4", "peregrine"],
2212                [":str", "8-12", "XX"]
2213            ]"#;
2214
2215        let expression = Parser::new().parse_str(pel).unwrap();
2216        let result = Runtime::new()
2217            .eval(&expression)
2218            .unwrap()
2219            .complete()
2220            .unwrap();
2221
2222        assert_eq!(result.as_str().unwrap(), "");
2223    }
2224
2225    #[test]
2226    fn substring_before_last_empty_string() {
2227        // DW: substringBeforeLast("peregrine", "")
2228        let pel = r#"
2229            [":apply", "0-55",
2230                [":ref", "0-38", "substringBeforeLast"],
2231                [":str", "39-50", "peregrine"],
2232                [":str", "52-54", ""]
2233            ]"#;
2234
2235        let expression = Parser::new().parse_str(pel).unwrap();
2236        let result = Runtime::new()
2237            .eval(&expression)
2238            .unwrap()
2239            .complete()
2240            .unwrap();
2241
2242        assert_eq!(result.as_str().unwrap(), "peregrin");
2243    }
2244
2245    #[test]
2246    fn upper_null() {
2247        // DW: upper(null)
2248        let pel = r#"
2249            [":apply", "5-7", 
2250                [":ref", "5-7", "upper"], 
2251                [":null", "0-4"]
2252            ]
2253        "#;
2254
2255        let expression = Parser::new().parse_str(pel).unwrap();
2256        let result = Runtime::new()
2257            .eval(&expression)
2258            .unwrap()
2259            .complete()
2260            .unwrap();
2261
2262        assert!(result.is_null());
2263    }
2264
2265    #[test]
2266    fn upper_string() {
2267        // DW: upper("Peregrine Expression Language")
2268        let pel = r#"
2269            [":apply", "5-7", 
2270                [":ref", "5-7", "upper"], 
2271                [":str", "0-4", "Peregrine Expression Language"]
2272            ]
2273        "#;
2274
2275        let expression = Parser::new().parse_str(pel).unwrap();
2276        let result = Runtime::new()
2277            .eval(&expression)
2278            .unwrap()
2279            .complete()
2280            .unwrap();
2281
2282        assert_eq!("PEREGRINE EXPRESSION LANGUAGE", result.as_str().unwrap());
2283    }
2284
2285    #[test]
2286    fn upper_boolean() {
2287        // DW: upper(true)
2288        let pel = r#"
2289            [":apply", "5-7", 
2290                [":ref", "5-7", "upper"], 
2291                [":bool", "0-4", "true"]
2292            ]
2293        "#;
2294
2295        let expression = Parser::new().parse_str(pel).unwrap();
2296        let result = Runtime::new()
2297            .eval(&expression)
2298            .unwrap()
2299            .complete()
2300            .unwrap();
2301
2302        assert_eq!("TRUE", result.as_str().unwrap());
2303    }
2304
2305    #[test]
2306    fn upper_number() {
2307        // DW: upper(123.4)
2308        let pel = r#"
2309            [":apply", "5-7", 
2310                [":ref", "5-7", "upper"], 
2311                [":nbr", "0-4", "123.4"]
2312            ]
2313        "#;
2314
2315        let expression = Parser::new().parse_str(pel).unwrap();
2316        let result = Runtime::new()
2317            .eval(&expression)
2318            .unwrap()
2319            .complete()
2320            .unwrap();
2321
2322        assert_eq!("123.4", result.as_str().unwrap());
2323    }
2324
2325    #[test]
2326    fn upper_array_fail() {
2327        // DW: upper(["accept", "accept-encoding", "user-agent"], "re")
2328        let pel = r#"
2329            [":apply", "5-7", 
2330                [":ref", "5-7", "upper"], 
2331                [":array", "0-41", 
2332                    [":str", "1-9", "accept"], 
2333                    [":str", "10-27", "accept-encoding"], 
2334                    [":str", "28-40", "user-agent"]
2335                ]
2336            ]
2337        "#;
2338
2339        let expression = Parser::new().parse_str(pel).unwrap();
2340        let error = Runtime::new().eval(&expression).unwrap_err();
2341
2342        assert_eq!(error.kind, RuntimeErrorKind::TypeMismatch);
2343    }
2344
2345    #[test]
2346    fn to_binary() {
2347        // DW: dw::util::Coercions::toBinary("some", "UTF-8")
2348        let pel = r#"[":apply", "0-46", [":ref", "0-29", "toBinary"], [":str", "30-36", "some"], [":str", "38-45", "UTF-8"]]"#;
2349
2350        let expression = Parser::new().parse_str(pel).unwrap();
2351        let result = Runtime::new()
2352            .eval(&expression)
2353            .unwrap()
2354            .complete()
2355            .unwrap();
2356
2357        assert_eq!(result.as_binary().unwrap(), "some".as_bytes());
2358    }
2359
2360    #[test]
2361    fn to_binary_not_enough_args() {
2362        // DW: dw::util::Coercions::toBinary("some")
2363        let pel = r#"[":apply", "0-46", [":ref", "0-29", "toBinary"], [":str", "30-36", "some"]]"#;
2364
2365        let expression = Parser::new().parse_str(pel).unwrap();
2366        let result = Runtime::new().eval(&expression).unwrap_err();
2367
2368        assert_eq!(result.kind, RuntimeErrorKind::NotEnoughArguments);
2369    }
2370
2371    #[test]
2372    fn to_binary_to_many_args() {
2373        // DW: dw::util::Coercions::toBinary("some", "UTF-8", "UTF-8")
2374        let pel = r#"[":apply", "0-46", [":ref", "0-29", "toBinary"], [":str", "30-36", "some"], [":str", "38-45", "UTF-8"], [":str", "38-45", "UTF-8"]]"#;
2375
2376        let expression = Parser::new().parse_str(pel).unwrap();
2377        let result = Runtime::new().eval(&expression).unwrap_err();
2378
2379        assert_eq!(result.kind, RuntimeErrorKind::TooManyArguments);
2380    }
2381
2382    #[test]
2383    fn to_binary_invalid_argument() {
2384        // DW: dw::util::Coercions::toBinary("some", "UTF-32")
2385        let pel = r#"[":apply", "0-46", [":ref", "0-29", "toBinary"], [":str", "30-36", "some"], [":str", "38-45", "UTF-32"]]"#;
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::TypeMismatch);
2391    }
2392
2393    #[test]
2394    fn from_binary() {
2395        // DW: dw::util::Coercions::toString(dw::util::Coercions::toBinary("some", "UTF-8"), "UTF-8")
2396        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"]]"#;
2397
2398        let expression = Parser::new().parse_str(pel).unwrap();
2399        let result = Runtime::new()
2400            .eval(&expression)
2401            .unwrap()
2402            .complete()
2403            .unwrap();
2404
2405        assert_eq!(result.as_str(), Some("some"));
2406    }
2407
2408    #[test]
2409    fn from_binary_not_enough_args() {
2410        // DW: dw::util::Coercions::toString(dw::util::Coercions::toBinary("some", "UTF-8"), "UTF-8")
2411        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"]]]"#;
2412
2413        let expression = Parser::new().parse_str(pel).unwrap();
2414        let result = Runtime::new().eval(&expression).unwrap_err();
2415
2416        assert_eq!(result.kind, RuntimeErrorKind::NotEnoughArguments);
2417    }
2418
2419    #[test]
2420    fn from_binary_to_many_args() {
2421        // DW: dw::util::Coercions::toString(dw::util::Coercions::toBinary("some", "UTF-8"), "UTF-8", "UTF-8")
2422        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"]]"#;
2423
2424        let expression = Parser::new().parse_str(pel).unwrap();
2425        let result = Runtime::new().eval(&expression).unwrap_err();
2426
2427        assert_eq!(result.kind, RuntimeErrorKind::TooManyArguments);
2428    }
2429
2430    #[test]
2431    fn from_binary_invalid_argument() {
2432        // DW: dw::util::Coercions::toString(dw::util::Coercions::toBinary("some", "UTF-8"), "UTF-32")
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"]], [":str", "78-85", "UTF-32"]]"#;
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::TypeMismatch);
2439    }
2440
2441    #[test]
2442    fn from_base64() {
2443        // DW: dw::core::Binaries::fromBase64("c29tZQ==")
2444        let pel =
2445            r#"[":apply", "0-46", [":ref", "0-29", "fromBase64"], [":str", "30-36", "c29tZQ=="]]"#;
2446
2447        let expression = Parser::new().parse_str(pel).unwrap();
2448        let result = Runtime::new()
2449            .eval(&expression)
2450            .unwrap()
2451            .complete()
2452            .unwrap();
2453
2454        assert_eq!(result.as_binary().unwrap(), "some".as_bytes());
2455    }
2456
2457    #[test]
2458    fn from_base64_not_enough_args() {
2459        // DW: dw::core::Binaries::fromBase64()
2460        let pel = r#"[":apply", "0-46", [":ref", "0-29", "fromBase64"]]"#;
2461
2462        let expression = Parser::new().parse_str(pel).unwrap();
2463        let result = Runtime::new().eval(&expression).unwrap_err();
2464
2465        assert_eq!(result.kind, RuntimeErrorKind::NotEnoughArguments);
2466    }
2467
2468    #[test]
2469    fn from_base64_to_many_args() {
2470        // DW: dw::core::Binaries::fromBase64("some", "UTF-8")
2471        let pel = r#"[":apply", "0-46", [":ref", "0-29", "fromBase64"], [":str", "30-36", "AA"], [":str", "38-45", "UTF-8"]]"#;
2472
2473        let expression = Parser::new().parse_str(pel).unwrap();
2474        let result = Runtime::new().eval(&expression).unwrap_err();
2475
2476        assert_eq!(result.kind, RuntimeErrorKind::TooManyArguments);
2477    }
2478
2479    #[test]
2480    fn from_base64_invalid_argument() {
2481        // DW: dw::core::Binaries::fromBase64("some")
2482        let pel = r#"[":apply", "0-46", [":ref", "0-29", "fromBase64"], [":str", "30-36", "0"]]"#;
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::TypeMismatch);
2488    }
2489
2490    #[test]
2491    fn to_base64() {
2492        // DW: dw::core::Binaries::toBase64(dw::core::Binaries::fromBase64("some", "UTF-8"), "UTF-8")
2493        let pel = r#"[":apply", "0-86", [":ref", "0-29", "toBase64"], [":apply", "30-76", [":ref", "30-59", "fromBase64"], [":str", "60-66", "AAAA"]]]"#;
2494
2495        let expression = Parser::new().parse_str(pel).unwrap();
2496        let result = Runtime::new()
2497            .eval(&expression)
2498            .unwrap()
2499            .complete()
2500            .unwrap();
2501
2502        assert_eq!(result.as_str(), Some("AAAA"));
2503    }
2504
2505    #[test]
2506    fn to_base64_not_enough_args() {
2507        // DW: dw::core::Binaries::toBase64()
2508        let pel = r#"[":apply", "0-86", [":ref", "0-29", "toBase64"]]"#;
2509
2510        let expression = Parser::new().parse_str(pel).unwrap();
2511        let result = Runtime::new().eval(&expression).unwrap_err();
2512
2513        assert_eq!(result.kind, RuntimeErrorKind::NotEnoughArguments);
2514    }
2515
2516    #[test]
2517    fn to_base64_to_many_args() {
2518        // DW: dw::core::Binaries::toBase64(dw::core::Binaries::fromBase64("some"), "UTF-8")
2519        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"]]"#;
2520
2521        let expression = Parser::new().parse_str(pel).unwrap();
2522        let result = Runtime::new().eval(&expression).unwrap_err();
2523
2524        assert_eq!(result.kind, RuntimeErrorKind::TooManyArguments);
2525    }
2526
2527    #[test]
2528    fn decode_basic_auth() {
2529        // dw::util::Coercions::toString(dw::core::Binaries::fromBase64(dw::core::Strings::substringAfter('basic c29tZTpvdGhlcg==', 'basic ')), 'UTF-8')
2530        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"]]"#;
2531
2532        let expression = Parser::new().parse_str(pel).unwrap();
2533        let result = Runtime::new()
2534            .eval(&expression)
2535            .unwrap()
2536            .complete()
2537            .unwrap();
2538
2539        assert_eq!(result.as_str().unwrap(), "some:other");
2540    }
2541
2542    #[test]
2543    fn decode_basic_auth_auto_coerce() {
2544        // dw::core::Strings::substringAfter(dw::core::Binaries::fromBase64("c29tZTpvdGhlcg=="), ':')
2545        let pel = r#"[":apply", "0-90", [":ref", "0-33", "substringAfter"], [":apply", "34-84", [":ref", "34-64", "fromBase64"], [":str", "65-83", "c29tZTpvdGhlcg=="]], [":str", "86-89", ":"]]"#;
2546
2547        let expression = Parser::new().parse_str(pel).unwrap();
2548        let result = Runtime::new()
2549            .eval(&expression)
2550            .unwrap()
2551            .complete()
2552            .unwrap();
2553
2554        assert_eq!(result.as_str().unwrap(), "other");
2555    }
2556
2557    #[test]
2558    fn encode_basic_auth() {
2559        // dw::core::Binaries::toBase64(dw::util::Coercions::toBinary('some:other', 'utf-8'))
2560        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"]]]"#;
2561
2562        let expression = Parser::new().parse_str(pel).unwrap();
2563        let result = Runtime::new()
2564            .eval(&expression)
2565            .unwrap()
2566            .complete()
2567            .unwrap();
2568
2569        assert_eq!(result.as_str().unwrap(), "c29tZTpvdGhlcg==");
2570    }
2571
2572    #[test]
2573    fn encode_basic_auth_auto_coerce() {
2574        // dw::core::Binaries::toBase64('some:other')
2575        let pel =
2576            r#"[":apply", "0-0", [":ref", "0-28", "toBase64"], [":str", "0-0", "some:other"]]"#;
2577
2578        let expression = Parser::new().parse_str(pel).unwrap();
2579        let result = Runtime::new()
2580            .eval(&expression)
2581            .unwrap()
2582            .complete()
2583            .unwrap();
2584
2585        assert_eq!(result.as_str().unwrap(), "c29tZTpvdGhlcg==");
2586    }
2587
2588    #[test]
2589    fn multimap_return_full() {
2590        let origin = "<envelope><some>b</some></envelope>";
2591        let value = DocumentBuilder::root(
2592            QualifiedName::new("", "envelope"),
2593            Rc::new(origin.to_string()),
2594            0..origin.len(),
2595        )
2596        .with_element(QualifiedName::new("", "some"), 10..24)
2597        .with_text("b")
2598        .finish_element()
2599        .finish_element()
2600        .build();
2601
2602        // DW: payload
2603        let pel = r#"[":ref", "0-0", "payload"]"#;
2604
2605        assert_eq!(
2606            evaluate(pel, value).as_doc_node().unwrap().content(),
2607            Some(origin.to_string())
2608        )
2609    }
2610
2611    #[test]
2612    fn multimap_return_obj() {
2613        let origin = "<envelope><some>b</some></envelope>";
2614        let value = DocumentBuilder::root(
2615            QualifiedName::new("", "envelope"),
2616            Rc::new(origin.to_string()),
2617            0..origin.len(),
2618        )
2619        .with_element(QualifiedName::new("", "some"), 10..24)
2620        .with_text("b")
2621        .finish_element()
2622        .finish_element()
2623        .build();
2624
2625        // DW: payload.envelope
2626        let pel = r#"
2627            [".", "0-0",
2628                [":ref", "0-0", "payload"],
2629                [":str", "0-0", "envelope"]
2630            ]
2631        "#;
2632
2633        assert_eq!(
2634            evaluate(pel, value).as_doc_node().unwrap().content(),
2635            Some("<?xml version='1.0' encoding='UTF-8'?>\n<some>\n    b\n</some>".to_string())
2636        )
2637    }
2638
2639    #[test]
2640    fn access_multimap_by_name() {
2641        let origin = "<envelope>b</envelope>";
2642        let value = DocumentBuilder::root(
2643            QualifiedName::new("", "envelope"),
2644            Rc::new(origin.to_string()),
2645            0..origin.len(),
2646        )
2647        .with_text("b")
2648        .finish_element()
2649        .build();
2650
2651        // DW: payload.envelope
2652        let pel = r#"
2653            ["==", "0-0",
2654                [":str", "0-0", "b"],
2655                [".", "0-0",
2656                    [":ref", "0-0", "payload"],
2657                    [":str", "0-0", "envelope"]
2658                ]
2659            ]
2660        "#;
2661
2662        evaluate_and_assert(pel, value, Value::bool(true))
2663    }
2664
2665    #[test]
2666    fn multimap_content() {
2667        let value = DocumentBuilder::root(
2668            QualifiedName::new("", "envelope"),
2669            Rc::new(Default::default()),
2670            0..0,
2671        )
2672        .with_element(QualifiedName::prefixed("s", "some-uri", "some"), 0..0)
2673        .with_attribute(
2674            QualifiedName::prefixed("s", "some-uri", "qualified"),
2675            "qualified",
2676        )
2677        .with_attribute(
2678            QualifiedName::prefixed("", "some-uri", "unqualified"),
2679            "unqualified",
2680        )
2681        .with_element(QualifiedName::prefixed("s", "some-uri", "first"), 0..0)
2682        .finish_element()
2683        .with_element(QualifiedName::prefixed("s", "some-uri", "second"), 0..0)
2684        .with_text("b")
2685        .finish_element()
2686        .finish_element()
2687        .finish_element()
2688        .build();
2689
2690        // DW: payload.envelope
2691        let pel = r#"
2692            [".", "0-0",
2693                [":ref", "0-0", "payload"],
2694                [":str", "0-0", "envelope"]
2695            ]
2696        "#;
2697
2698        assert_eq!(
2699            evaluate(pel, value.clone())
2700                .as_doc_node()
2701                .unwrap()
2702                .content(),
2703            Some(
2704                r#"<?xml version='1.0' encoding='UTF-8'?>
2705<s:some xmlns:s="some-uri" s:qualified="qualified" xmlns="some-uri" unqualified="unqualified">
2706    <s:first/>
2707    <s:second>
2708        b
2709    </s:second>
2710</s:some>"#
2711                    .to_string()
2712            )
2713        );
2714
2715        // DW: payload.envelope.some
2716        let pel = r#"
2717            [".", "0-0",
2718                [".", "0-0",
2719                    [":ref", "0-0", "payload"],
2720                    [":str", "0-0", "envelope"]
2721                ],
2722                [":str", "0-0", "some"]
2723            ]
2724        "#;
2725
2726        assert_eq!(evaluate(pel, value).as_doc_node().unwrap().content(), None)
2727    }
2728
2729    #[test]
2730    fn access_multimap_by_qname() {
2731        // <envelope xmlns="http://test.com">b</envelope>
2732        let value = DocumentBuilder::root(
2733            QualifiedName::new("http://test.com", "envelope"),
2734            Rc::new("".to_string()),
2735            0..0,
2736        )
2737        .with_text("b")
2738        .finish_element()
2739        .build();
2740
2741        // DW: "b" == payload.http://test.com#envelope
2742        let pel = r#"
2743            ["==", "0-0",
2744                [":str", "0-0", "b"],
2745                [".", "0-0", [":ref", "0-0", "payload"], [":name", "0-0", [":ns", "0-0", "http://test.com"], [":str", "0-0", "envelope"]]]
2746            ]
2747        "#;
2748
2749        evaluate_and_assert(pel, value, Value::bool(true))
2750    }
2751
2752    #[test]
2753    fn access_multimap_attribute_by_name() {
2754        // <envelope xmlns="http://test.com" xmlns:a="http://another.com" a:attr="another" attr="JJ">b</envelope>
2755        let value = DocumentBuilder::root(
2756            QualifiedName::new("http://test.com", "envelope"),
2757            Rc::new("".to_string()),
2758            0..0,
2759        )
2760        .with_attribute(QualifiedName::new("http://another.com", "attr"), "another")
2761        .with_attribute(QualifiedName::new("http://test.com", "attr"), "JJ")
2762        .with_text("b")
2763        .finish_element()
2764        .build();
2765
2766        // DW: "b" == payload.http://test.com#envelope
2767        let pel = r#"
2768            ["==", "0-0",
2769                [":str", "0-0", "another"],
2770                ["@", "0-0", [".", "0-0", [":ref", "0-0", "payload"], [":str", "0-0", "envelope"]], [":str", "0-0", "attr"]]
2771            ]
2772        "#;
2773
2774        evaluate_and_assert(pel, value, Value::bool(true))
2775    }
2776
2777    #[test]
2778    fn access_multimap_attribute_by_qname() {
2779        // <envelope xmlns="http://test.com" xmlns:a="http://another.com" a:attr="another" attr="JJ">b</envelope>
2780        let value = DocumentBuilder::root(
2781            QualifiedName::new("http://test.com", "envelope"),
2782            Rc::new("".to_string()),
2783            0..0,
2784        )
2785        .with_attribute(QualifiedName::new("http://another.com", "attr"), "another")
2786        .with_attribute(QualifiedName::new("http://test.com", "attr"), "JJ")
2787        .with_text("b")
2788        .finish_element()
2789        .build();
2790
2791        // DW: "b" == payload.http://test.com#envelope
2792        let pel = r#"
2793            ["==", "0-0",
2794                [":str", "0-0", "JJ"],
2795                ["@", "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"]]]
2796            ]
2797        "#;
2798
2799        evaluate_and_assert(pel, value, Value::bool(true))
2800    }
2801
2802    #[test]
2803    fn access_multimap_by_non_existing_key() {
2804        // <envelope>b</envelope>
2805        let value = DocumentBuilder::root(
2806            QualifiedName::new("", "envelope"),
2807            Rc::new("".to_string()),
2808            0..0,
2809        )
2810        .with_text("b")
2811        .finish_element()
2812        .build();
2813
2814        // DW: payload.envelope == b
2815        let pel = r#"
2816            ["==", "0-0",
2817                [".", "0-0",
2818                    [":ref", "0-0", "payload"],
2819                    [":str", "0-0", "env"]
2820                ],
2821                [":null", "0-0"]
2822            ]
2823        "#;
2824        evaluate_and_assert(pel, value, Value::bool(true))
2825    }
2826
2827    #[test]
2828    fn access_multimap_by_repeated_key() {
2829        // <envelope>
2830        //      <a>a1</a>
2831        //      <a>a2</a>
2832        // </envelope>
2833        let value = DocumentBuilder::root(
2834            QualifiedName::new("", "envelope"),
2835            Rc::new("".to_string()),
2836            0..0,
2837        )
2838        .with_element(QualifiedName::new("", "a"), 0..0)
2839        .with_text("a1")
2840        .finish_element()
2841        .with_element(QualifiedName::new("", "a"), 0..0)
2842        .with_text("a2")
2843        .finish_element()
2844        .finish_element()
2845        .build();
2846
2847        // DW: payload.envelope.a == b
2848        let pel = r#"
2849            ["==", "0-0",
2850                [".", "0-0",
2851                    [".", "0-0",
2852                        [":ref", "0-0", "payload"],
2853                        [":str", "0-0", "envelope"]
2854                    ],
2855                    [":str", "0-0", "a"]
2856                ],
2857                [":str", "0-0", "a1"]
2858            ]
2859        "#;
2860        evaluate_and_assert(pel, value, Value::bool(true))
2861    }
2862
2863    #[test]
2864    fn compare_multimaps() {
2865        // <envelope>
2866        //      <a1>a<b/></a1>
2867        //      <a2>a<b/></a2>
2868        // </envelope>
2869        let value = DocumentBuilder::root(
2870            QualifiedName::new("", "envelope"),
2871            Rc::new("".to_string()),
2872            0..0,
2873        )
2874        .with_element(QualifiedName::new("", "a1"), 0..0)
2875        .with_text("a")
2876        .with_element(QualifiedName::new("", "b"), 0..0)
2877        .finish_element()
2878        .finish_element()
2879        .with_element(QualifiedName::new("", "a2"), 0..0)
2880        .with_text("a")
2881        .with_element(QualifiedName::new("", "b"), 0..0)
2882        .finish_element()
2883        .finish_element()
2884        .finish_element()
2885        .build();
2886
2887        // DW: payload.envelope.a1 == payload.envelope.a2
2888        let pel = r#"
2889            ["==", "0-0",
2890                [".", "0-0",
2891                    [".", "0-0",
2892                        [":ref", "0-0", "payload"],
2893                        [":str", "0-0", "envelope"]
2894                    ],
2895                    [":str", "0-0", "a1"]
2896                ],
2897                [".", "0-0",
2898                    [".", "0-0",
2899                        [":ref", "0-0", "payload"],
2900                        [":str", "0-0", "envelope"]
2901                    ],
2902                    [":str", "0-0", "a2"]
2903                ]
2904            ]
2905        "#;
2906        evaluate_and_assert(pel, value, Value::bool(true))
2907    }
2908
2909    #[test]
2910    fn access_multimap_by_index() {
2911        // <envelope>b</envelope>
2912        let value = DocumentBuilder::root(
2913            QualifiedName::new("", "envelope"),
2914            Rc::new("".to_string()),
2915            0..0,
2916        )
2917        .with_text("b")
2918        .finish_element()
2919        .build();
2920
2921        // DW: payload.envelope == b
2922        let pel = r#"
2923            ["==", "0-0",
2924                [".", "0-0",
2925                    [":ref", "0-0", "payload"],
2926                    [":nbr", "0-0", "0"]
2927                ],
2928                [":str", "0-0", "b"]
2929            ]
2930        "#;
2931        evaluate_and_assert(pel, value, Value::bool(true))
2932    }
2933
2934    #[test]
2935    fn access_multimap_by_non_existing_index() {
2936        // <envelope>b</envelope>
2937        let value = DocumentBuilder::root(
2938            QualifiedName::new("", "envelope"),
2939            Rc::new("".to_string()),
2940            0..0,
2941        )
2942        .with_text("b")
2943        .finish_element()
2944        .build();
2945
2946        // DW: payload.envelope == b
2947        let pel = r#"
2948            ["==", "0-0",
2949                [".", "0-0",
2950                    [":ref", "0-0", "payload"],
2951                    [":nbr", "0-0", "1"]
2952                ],
2953                [":null", "0-0"]
2954            ]
2955        "#;
2956        evaluate_and_assert(pel, value, Value::bool(true))
2957    }
2958
2959    #[test]
2960    fn access_multimap_multivalue_by_name() {
2961        // <envelope xmlns="http://test.com"><b>1</b><b>2</b></envelope>
2962        let value = DocumentBuilder::root(
2963            QualifiedName::new("http://test.com", "envelope"),
2964            Rc::new("".to_string()),
2965            0..0,
2966        )
2967        .with_element(QualifiedName::new("http://test.com", "b"), 0..0)
2968        .with_text("1")
2969        .finish_element()
2970        .with_element(QualifiedName::new("http://test.com", "b"), 0..0)
2971        .with_text("2")
2972        .finish_element()
2973        .finish_element()
2974        .build();
2975
2976        let pel = r#"
2977            ["*", "0-0", [".", "0-0", [":ref", "0-0", "payload"], [":str", "0-0", "envelope"]], [":str", "0-0", "b"]]
2978        "#;
2979
2980        let expected = Value::array(vec![Value::string("1"), Value::string("2")]);
2981        evaluate_and_assert(pel, value, expected)
2982    }
2983
2984    #[test]
2985    fn access_multimap_multivalue_by_qname() {
2986        // <envelope xmlns="http://test.com" xmlns:o="http://other.com"><b>1</b><o:b>2</o:b><b>3</b></envelope>
2987        let value = DocumentBuilder::root(
2988            QualifiedName::new("http://test.com", "envelope"),
2989            Rc::new("".to_string()),
2990            0..0,
2991        )
2992        .with_element(QualifiedName::new("http://test.com", "b"), 0..0)
2993        .with_text("1")
2994        .finish_element()
2995        .with_element(QualifiedName::new("http://other.com", "b"), 0..0)
2996        .with_text("2")
2997        .finish_element()
2998        .with_element(QualifiedName::new("http://test.com", "b"), 0..0)
2999        .with_text("3")
3000        .finish_element()
3001        .finish_element()
3002        .build();
3003
3004        let pel = r#"
3005            ["*", "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"]]]
3006        "#;
3007
3008        let expected = Value::array(vec![Value::string("1"), Value::string("3")]);
3009        evaluate_and_assert(pel, value, expected)
3010    }
3011
3012    #[cfg(feature = "experimental_coerced_type")]
3013    #[test]
3014    fn multimap_root_coerced() {
3015        let origin = "<envelope><some>b</some></envelope>";
3016        let value = DocumentBuilder::root(
3017            QualifiedName::new("", "envelope"),
3018            Rc::new(origin.to_string()),
3019            0..origin.len(),
3020        )
3021        .with_element(QualifiedName::new("", "some"), 10..24)
3022        .with_text("b")
3023        .finish_element()
3024        .finish_element()
3025        .build();
3026
3027        // DW: payload ++ "envelope"
3028        let pel = r#"[":apply", "0-0", [":ref", "0-0", "++"], [":ref", "0-0", "payload"], [":str", "0-0", "envelope"]]"#;
3029
3030        assert_eq!(
3031            evaluate(pel, value.clone()).as_str().unwrap(),
3032            origin.to_string() + "envelope"
3033        );
3034
3035        // DW: "envelope" ++ envelope
3036        let pel = r#"[":apply", "0-0", [":ref", "0-0", "++"], [":str", "0-0", "envelope"], [":ref", "0-0", "payload"]]"#;
3037        assert_eq!(
3038            evaluate(pel, value.clone()).as_str().unwrap(),
3039            "envelope".to_string() + origin
3040        );
3041
3042        // DW: payload == "<envelope><some>b</some></envelope>"
3043        let pel = r#"
3044            ["==", "0-0",
3045                [":ref", "0-0", "payload"],
3046                [":str", "0-0", "<envelope><some>b</some></envelope>"]
3047            ]
3048        "#;
3049        assert!(evaluate(pel, value).as_bool().unwrap());
3050    }
3051
3052    #[test]
3053    fn multimap_not_coerced() {
3054        let origin = "<envelope><some>b</some></envelope>";
3055        let value = DocumentBuilder::root(
3056            QualifiedName::new("", "envelope"),
3057            Rc::new(origin.to_string()),
3058            0..origin.len(),
3059        )
3060        .with_element(QualifiedName::new("", "some"), 10..24)
3061        .with_text("b")
3062        .finish_element()
3063        .finish_element()
3064        .build();
3065
3066        // DW: payload.envelope ++ "envelope"
3067        let pel = r#"[":apply", "0-0",
3068            [":ref", "0-0", "++"],
3069            [".", "0-0",
3070                [":ref", "0-0", "payload"],
3071                [":str", "0-0", "envelope"]
3072            ],
3073            [":str", "0-0", "envelope"]
3074        ]"#;
3075
3076        assert_eq!(
3077            &RuntimeErrorKind::TypeMismatch,
3078            evaluate_error(pel, value.clone()).kind(),
3079        );
3080
3081        // DW: payload == "<envelope><some>b</some></envelope>"
3082        let pel = r#"
3083            ["==", "0-0",
3084                [".", "0-0",
3085                   [":ref", "0-0", "payload"],
3086                    [":str", "0-0", "envelope"]
3087                ],
3088                [":str", "0-0", "<some>b</some>"]
3089            ]
3090        "#;
3091        assert!(!evaluate(pel, value).as_bool().unwrap());
3092    }
3093
3094    #[cfg(feature = "experimental_coerced_type")]
3095    #[test]
3096    fn object_root_coerced() {
3097        let origin = r#"{"envelope":{"some":"value"}}"#;
3098        let value = Value::coerced_object(
3099            HashMap::from([(
3100                "envelope".to_string(),
3101                Value::object(HashMap::from([(
3102                    "some".to_string(),
3103                    Value::string("value"),
3104                )])),
3105            )]),
3106            Rc::new(origin.to_string()),
3107        );
3108
3109        // DW: payload ++ "envelope"
3110        let pel = r#"[":apply", "0-0", [":ref", "0-0", "++"], [":ref", "0-0", "payload"], [":str", "0-0", "envelope"]]"#;
3111
3112        assert_eq!(
3113            evaluate(pel, value.clone()).as_str().unwrap(),
3114            origin.to_string() + "envelope"
3115        );
3116
3117        // DW: "envelope" ++ envelope
3118        let pel = r#"[":apply", "0-0", [":ref", "0-0", "++"], [":str", "0-0", "envelope"], [":ref", "0-0", "payload"]]"#;
3119        assert_eq!(
3120            evaluate(pel, value.clone()).as_str().unwrap(),
3121            "envelope".to_string() + origin
3122        );
3123
3124        // DW: payload == "{"envelope":"value"}"
3125        let pel = r#"
3126            ["==", "0-0",
3127                [":ref", "0-0", "payload"],
3128                [":str", "0-0", "{\"envelope\":{\"some\":\"value\"}}"]
3129            ]
3130        "#;
3131        assert!(evaluate(pel, value).as_bool().unwrap());
3132    }
3133
3134    #[test]
3135    fn object_not_coerced() {
3136        #[cfg(feature = "experimental_coerced_type")]
3137        let origin = r#"{"envelope":{"some":"value"}}"#;
3138
3139        let map = HashMap::from([(
3140            "envelope".to_string(),
3141            Value::object(HashMap::from([(
3142                "some".to_string(),
3143                Value::string("value"),
3144            )])),
3145        )]);
3146
3147        #[cfg(feature = "experimental_coerced_type")]
3148        let value = Value::coerced_object(map, Rc::new(origin.to_string()));
3149
3150        #[cfg(not(feature = "experimental_coerced_type"))]
3151        let value = Value::object(map);
3152
3153        // DW: payload.envelope ++ "envelope"
3154        let pel = r#"[":apply", "0-0",
3155            [":ref", "0-0", "++"],
3156            [".", "0-0",
3157                [":ref", "0-0", "payload"],
3158                [":str", "0-0", "envelope"]
3159            ],
3160            [":str", "0-0", "envelope"]
3161        ]"#;
3162
3163        assert_eq!(
3164            &RuntimeErrorKind::TypeMismatch,
3165            evaluate_error(pel, value.clone()).kind(),
3166        );
3167
3168        // DW: payload == "{"some":"value"}"
3169        let pel = r#"
3170            ["==", "0-0",
3171                [".", "0-0",
3172                   [":ref", "0-0", "payload"],
3173                    [":str", "0-0", "envelope"]
3174                ],
3175                [":str", "0-0", "{\"some\":\"value\"}"]
3176            ]
3177        "#;
3178        assert!(!evaluate(pel, value).as_bool().unwrap());
3179    }
3180
3181    fn evaluate_error(pel: &str, payload: Value) -> RuntimeError {
3182        let context: Prelude = HashMap::from([("payload", payload)]);
3183
3184        let expression = Parser::new().parse_str(pel).unwrap();
3185        Runtime::new()
3186            .eval_with_context(&expression, &context)
3187            .err()
3188            .unwrap()
3189    }
3190
3191    fn evaluate(pel: &str, payload: Value) -> Value {
3192        let context: Prelude = HashMap::from([("payload", payload)]);
3193
3194        let expression = Parser::new().parse_str(pel).unwrap();
3195        Runtime::new()
3196            .eval_with_context(&expression, &context)
3197            .unwrap()
3198            .complete()
3199            .unwrap()
3200    }
3201
3202    fn evaluate_and_assert(pel: &str, payload: Value, expected: Value) {
3203        assert_eq!(evaluate(pel, payload), expected)
3204    }
3205
3206    mod partial_evaluation {
3207        use std::collections::HashMap;
3208
3209        use crate::{
3210            expression::Symbol,
3211            parser::Parser,
3212            runtime::{value::Value, Binding, Context, Runtime, ValueHandler},
3213            Reference,
3214        };
3215
3216        struct TestContextChain {
3217            contexts: Vec<HashMap<String, Value>>,
3218        }
3219
3220        impl TestContextChain {
3221            fn new<const N: usize>(entries: [(&str, Value); N]) -> Self {
3222                Self { contexts: vec![] }.then(entries)
3223            }
3224
3225            fn then<const N: usize>(mut self, entries: [(&str, Value); N]) -> Self {
3226                self.contexts
3227                    .push(entries.map(|(k, v)| (k.to_string(), v)).into());
3228                self
3229            }
3230
3231            fn next(mut self) -> Self {
3232                self.contexts.remove(0);
3233                self
3234            }
3235        }
3236
3237        impl Context for TestContextChain {
3238            fn resolve(&self, symbol: &Symbol) -> Binding {
3239                let s = symbol.as_str();
3240                match self.contexts.first().expect("Empty context").get(s) {
3241                    Some(value) => Binding::Available(value.clone()),
3242                    None => {
3243                        if self.contexts.iter().any(|c| c.contains_key(s)) {
3244                            Binding::Pending
3245                        } else {
3246                            Binding::Unknown
3247                        }
3248                    }
3249                }
3250            }
3251
3252            fn value_handler(&self, _reference: Reference) -> Option<&dyn ValueHandler> {
3253                None
3254            }
3255        }
3256
3257        #[test]
3258        fn if_else_pending_condition() {
3259            let runtime = Runtime::new();
3260            let parser = Parser::new();
3261
3262            let context_1 = TestContextChain::new([("a", Value::string("ctx1".to_string()))])
3263                .then([("condition", Value::bool(true))])
3264                .then([("b", Value::string("ctx3".to_string()))]);
3265
3266            // DW: if (condition) a else b
3267            let pel_1 = r#"[":if", "0-23", [":ref", "4-9", "condition"], [":ref", "11-14", "a"], [":ref", "20-23", "b"]]"#;
3268
3269            let expression_1 = parser.parse_str(pel_1).unwrap();
3270            let expression_2 = runtime
3271                .eval_with_context(&expression_1, &context_1)
3272                .unwrap()
3273                .partial()
3274                .unwrap();
3275
3276            // DW: if (condition) "ctx1" else b
3277            let pel_2 = r#"[":if", "0-23", [":ref", "4-9", "condition"], [":str", "11-14", "ctx1"], [":ref", "20-23", "b"]]"#;
3278
3279            assert_eq!(expression_2, parser.parse_str(pel_2).unwrap());
3280
3281            let context_2 = context_1.next();
3282            let result = runtime
3283                .eval_with_context(&expression_2, &context_2)
3284                .unwrap()
3285                .complete()
3286                .unwrap();
3287
3288            assert_eq!(result.as_str().unwrap(), "ctx1");
3289        }
3290
3291        #[test]
3292        fn if_else_pending_true_branch() {
3293            let runtime = Runtime::new();
3294            let parser = Parser::new();
3295
3296            let context_1 = TestContextChain::new([("a", Value::string("ctx1".to_string()))])
3297                .then([("b", Value::string("ctx2".to_string()))]);
3298
3299            // DW: if (false) a else b
3300            let pel_1 = r#"[":if", "0-23", [":bool", "4-9", "true"], [":ref", "11-14", "b"], [":ref", "20-23", "a"]]"#;
3301
3302            let expression_1 = parser.parse_str(pel_1).unwrap();
3303            let expression_2 = runtime
3304                .eval_with_context(&expression_1, &context_1)
3305                .unwrap()
3306                .partial()
3307                .unwrap();
3308
3309            let pel_2 = r#"[":ref", "11-14", "b"]"#;
3310
3311            assert_eq!(expression_2, parser.parse_str(pel_2).unwrap());
3312
3313            let context_2 = context_1.next();
3314            let result = runtime
3315                .eval_with_context(&expression_2, &context_2)
3316                .unwrap()
3317                .complete()
3318                .unwrap();
3319
3320            assert_eq!(result.as_str().unwrap(), "ctx2");
3321        }
3322
3323        #[test]
3324        fn if_else_pending_false_branch() {
3325            let runtime = Runtime::new();
3326            let parser = Parser::new();
3327
3328            let context_1 = TestContextChain::new([("a", Value::string("ctx1".to_string()))])
3329                .then([("b", Value::string("ctx2".to_string()))]);
3330
3331            // DW: if (false) a else b
3332            let pel_1 = r#"[":if", "0-23", [":bool", "4-9", "false"], [":ref", "11-14", "a"], [":ref", "20-23", "b"]]"#;
3333
3334            let expression_1 = parser.parse_str(pel_1).unwrap();
3335            let expression_2 = runtime
3336                .eval_with_context(&expression_1, &context_1)
3337                .unwrap()
3338                .partial()
3339                .unwrap();
3340
3341            let pel_2 = r#"[":ref", "20-23", "b"]"#;
3342
3343            assert_eq!(expression_2, parser.parse_str(pel_2).unwrap());
3344
3345            let context_2 = context_1.next();
3346            let result = runtime
3347                .eval_with_context(&expression_2, &context_2)
3348                .unwrap()
3349                .complete()
3350                .unwrap();
3351
3352            assert_eq!(result.as_str().unwrap(), "ctx2");
3353        }
3354
3355        #[test]
3356        fn default_left_unavailable() {
3357            let runtime = Runtime::new();
3358            let parser = Parser::new();
3359
3360            let context_1 = TestContextChain::new([("right", Value::string("ctx1".to_string()))])
3361                .then([("left", Value::null())]);
3362
3363            // DW: left default right
3364            let pel_1 = r#"
3365                [":default", "0-6",
3366                    [":ref", "0-1", "left"],
3367                    [":ref", "5-6", "right"]
3368                ]
3369            "#;
3370
3371            let expression_1 = parser.parse_str(pel_1).unwrap();
3372            let expression_2 = runtime
3373                .eval_with_context(&expression_1, &context_1)
3374                .unwrap()
3375                .partial()
3376                .unwrap();
3377
3378            // DW: left default "ctx1"
3379            let pel_2 = r#"
3380                [":default", "0-6",
3381                    [":ref", "0-1", "left"],
3382                    [":str", "5-6", "ctx1"]
3383                ]
3384            "#;
3385
3386            assert_eq!(expression_2, parser.parse_str(pel_2).unwrap());
3387
3388            let context_2 = context_1.next();
3389            let result = runtime
3390                .eval_with_context(&expression_2, &context_2)
3391                .unwrap()
3392                .complete()
3393                .unwrap();
3394
3395            assert_eq!(result.as_str().unwrap(), "ctx1");
3396        }
3397
3398        #[test]
3399        fn default_right_unavailable() {
3400            let runtime = Runtime::new();
3401            let parser = Parser::new();
3402
3403            let context_1 = TestContextChain::new([("left", Value::null())])
3404                .then([("right", Value::string("ctx1".to_string()))]);
3405
3406            // DW: left default right
3407            let pel_1 = r#"
3408                [":default", "0-6",
3409                    [":ref", "0-1", "left"],
3410                    [":ref", "5-6", "right"]
3411                ]
3412            "#;
3413
3414            let expression_1 = parser.parse_str(pel_1).unwrap();
3415            let expression_2 = runtime
3416                .eval_with_context(&expression_1, &context_1)
3417                .unwrap()
3418                .partial()
3419                .unwrap();
3420
3421            // DW: right
3422            let pel_2 = r#"
3423                [":ref", "5-6", "right"]
3424            "#;
3425
3426            assert_eq!(expression_2, parser.parse_str(pel_2).unwrap());
3427
3428            let context_2 = context_1.next();
3429            let result = runtime
3430                .eval_with_context(&expression_2, &context_2)
3431                .unwrap()
3432                .complete()
3433                .unwrap();
3434
3435            assert_eq!(result.as_str().unwrap(), "ctx1");
3436        }
3437    }
3438}