Skip to main content

camel_language_simple/
lib.rs

1mod evaluator;
2mod parser;
3
4use std::sync::Arc;
5
6use camel_language_api::{Exchange, Expression, Language, LanguageError, Predicate, Value};
7
8pub type ResolverFn = Arc<dyn Fn(&str) -> Option<Arc<dyn Language>> + Send + Sync>;
9
10pub struct SimpleLanguage {
11    resolver: Option<ResolverFn>,
12}
13
14struct SimpleExpression {
15    expr: parser::Expr,
16    resolver: Option<ResolverFn>,
17}
18
19struct SimplePredicate {
20    expr: parser::Expr,
21    resolver: Option<ResolverFn>,
22}
23
24impl SimpleLanguage {
25    pub fn new() -> Self {
26        Self { resolver: None }
27    }
28
29    pub fn with_resolver(resolver: ResolverFn) -> Self {
30        Self {
31            resolver: Some(resolver),
32        }
33    }
34}
35
36impl Default for SimpleLanguage {
37    fn default() -> Self {
38        Self::new()
39    }
40}
41
42impl Expression for SimpleExpression {
43    fn evaluate(&self, exchange: &Exchange) -> Result<Value, LanguageError> {
44        evaluator::evaluate(&self.expr, exchange, &self.resolver)
45    }
46}
47
48impl Predicate for SimplePredicate {
49    fn matches(&self, exchange: &Exchange) -> Result<bool, LanguageError> {
50        let val = evaluator::evaluate(&self.expr, exchange, &self.resolver)?;
51        Ok(match &val {
52            Value::Bool(b) => *b,
53            Value::Null => false,
54            _ => true,
55        })
56    }
57}
58
59impl Language for SimpleLanguage {
60    fn name(&self) -> &'static str {
61        "simple"
62    }
63
64    fn create_expression(&self, script: &str) -> Result<Box<dyn Expression>, LanguageError> {
65        let ast = parser::parse(script)?;
66        Ok(Box::new(SimpleExpression {
67            expr: ast,
68            resolver: self.resolver.clone(),
69        }))
70    }
71
72    fn create_predicate(&self, script: &str) -> Result<Box<dyn Predicate>, LanguageError> {
73        let ast = parser::parse(script)?;
74        Ok(Box::new(SimplePredicate {
75            expr: ast,
76            resolver: self.resolver.clone(),
77        }))
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::SimpleLanguage;
84    use camel_language_api::Language;
85    use camel_language_api::{Exchange, Message, Value};
86
87    fn exchange_with_header(key: &str, val: &str) -> Exchange {
88        let mut msg = Message::default();
89        msg.set_header(key, Value::String(val.to_string()));
90        Exchange::new(msg)
91    }
92
93    fn exchange_with_body(body: &str) -> Exchange {
94        Exchange::new(Message::new(body))
95    }
96
97    #[test]
98    fn test_header_equals_string() {
99        let lang = SimpleLanguage::new();
100        let pred = lang.create_predicate("${header.type} == 'order'").unwrap();
101        let ex = exchange_with_header("type", "order");
102        assert!(pred.matches(&ex).unwrap());
103    }
104
105    #[test]
106    fn test_header_not_equals() {
107        let lang = SimpleLanguage::new();
108        let pred = lang.create_predicate("${header.type} != 'order'").unwrap();
109        let ex = exchange_with_header("type", "invoice");
110        assert!(pred.matches(&ex).unwrap());
111    }
112
113    #[test]
114    fn test_body_contains() {
115        let lang = SimpleLanguage::new();
116        let pred = lang.create_predicate("${body} contains 'hello'").unwrap();
117        let ex = exchange_with_body("say hello world");
118        assert!(pred.matches(&ex).unwrap());
119    }
120
121    #[test]
122    fn test_header_null_check() {
123        let lang = SimpleLanguage::new();
124        let pred = lang.create_predicate("${header.missing} == null").unwrap();
125        let ex = exchange_with_body("anything");
126        assert!(pred.matches(&ex).unwrap());
127    }
128
129    #[test]
130    fn test_header_not_null() {
131        let lang = SimpleLanguage::new();
132        let pred = lang.create_predicate("${header.type} != null").unwrap();
133        let ex = exchange_with_header("type", "order");
134        assert!(pred.matches(&ex).unwrap());
135    }
136
137    #[test]
138    fn test_expression_header_value() {
139        let lang = SimpleLanguage::new();
140        let expr = lang.create_expression("${header.type}").unwrap();
141        let ex = exchange_with_header("type", "order");
142        let val = expr.evaluate(&ex).unwrap();
143        assert_eq!(val, Value::String("order".to_string()));
144    }
145
146    #[test]
147    fn test_expression_body() {
148        let lang = SimpleLanguage::new();
149        let expr = lang.create_expression("${body}").unwrap();
150        let ex = exchange_with_body("hello");
151        let val = expr.evaluate(&ex).unwrap();
152        assert_eq!(val, Value::String("hello".to_string()));
153    }
154
155    #[test]
156    fn test_numeric_comparison() {
157        let lang = SimpleLanguage::new();
158        let pred = lang.create_predicate("${header.age} > 18").unwrap();
159        let mut ex = Exchange::new(Message::default());
160        ex.input.set_header("age", Value::Number(25.into()));
161        assert!(pred.matches(&ex).unwrap());
162    }
163
164    // --- Edge case tests ---
165
166    #[test]
167    fn test_empty_body() {
168        let lang = SimpleLanguage::new();
169        let expr = lang.create_expression("${body}").unwrap();
170        let ex = Exchange::new(Message::default());
171        let val = expr.evaluate(&ex).unwrap();
172        assert_eq!(val, Value::Null);
173    }
174
175    #[test]
176    fn test_parse_error_unrecognized_token() {
177        // A pure `${...}` token that doesn't match any known form is a parse error.
178        // For example `${unknown}` is not a valid Simple expression.
179        let lang = SimpleLanguage::new();
180        let result = lang.create_expression("${unknown}");
181        assert!(result.is_err(), "unknown token should be a parse error");
182    }
183
184    #[test]
185    fn test_empty_header_key_is_parse_error() {
186        let lang = SimpleLanguage::new();
187        let result = lang.create_expression("${header.}");
188        let err = result.err().expect("should be a parse error");
189        let err = format!("{err}");
190        assert!(
191            err.contains("empty"),
192            "error should mention empty key, got: {err}"
193        );
194    }
195
196    #[test]
197    fn test_empty_exchange_property_key_is_parse_error() {
198        let lang = SimpleLanguage::new();
199        let result = lang.create_expression("${exchangeProperty.}");
200        let err = result.err().expect("should be a parse error");
201        let err = format!("{err}");
202        assert!(
203            err.contains("empty"),
204            "error should mention empty key, got: {err}"
205        );
206    }
207
208    #[test]
209    fn test_missing_header_returns_null() {
210        let lang = SimpleLanguage::new();
211        let expr = lang.create_expression("${header.nonexistent}").unwrap();
212        let ex = exchange_with_body("anything");
213        let val = expr.evaluate(&ex).unwrap();
214        assert_eq!(val, Value::Null);
215    }
216
217    #[test]
218    fn test_exchange_property_expression() {
219        let lang = SimpleLanguage::new();
220        let expr = lang
221            .create_expression("${exchangeProperty.myProp}")
222            .unwrap();
223        let mut ex = exchange_with_body("test");
224        ex.set_property("myProp".to_string(), Value::String("propVal".to_string()));
225        let val = expr.evaluate(&ex).unwrap();
226        assert_eq!(val, Value::String("propVal".to_string()));
227    }
228
229    #[test]
230    fn test_missing_property_returns_null() {
231        let lang = SimpleLanguage::new();
232        let expr = lang
233            .create_expression("${exchangeProperty.missing}")
234            .unwrap();
235        let ex = exchange_with_body("test");
236        let val = expr.evaluate(&ex).unwrap();
237        assert_eq!(val, Value::Null);
238    }
239
240    #[test]
241    fn test_string_literal_expression() {
242        let lang = SimpleLanguage::new();
243        let expr = lang.create_expression("'hello'").unwrap();
244        let ex = exchange_with_body("test");
245        let val = expr.evaluate(&ex).unwrap();
246        assert_eq!(val, Value::String("hello".to_string()));
247    }
248
249    #[test]
250    fn test_double_quoted_literal_unescapes_newline() {
251        let lang = SimpleLanguage::new();
252        let expr = lang.create_expression("\"line1\\nline2\"").unwrap();
253        let ex = exchange_with_body("test");
254        let val = expr.evaluate(&ex).unwrap();
255        assert_eq!(val, Value::String("line1\nline2".to_string()));
256    }
257
258    #[test]
259    fn test_double_quoted_literal_unescapes_tab_and_quote() {
260        let lang = SimpleLanguage::new();
261        let expr = lang.create_expression("\"col1\\t\\\"quoted\\\"\"").unwrap();
262        let ex = exchange_with_body("test");
263        let val = expr.evaluate(&ex).unwrap();
264        assert_eq!(val, Value::String("col1\t\"quoted\"".to_string()));
265    }
266
267    #[test]
268    fn test_double_quoted_literal_unescapes_backspace_formfeed_and_slash() {
269        let lang = SimpleLanguage::new();
270        let expr = lang.create_expression("\"a\\bb\\fc\\/d\"").unwrap();
271        let ex = exchange_with_body("test");
272        let val = expr.evaluate(&ex).unwrap();
273        assert_eq!(val, Value::String("a\u{0008}b\u{000C}c/d".to_string()));
274    }
275
276    #[test]
277    fn test_null_expression() {
278        let lang = SimpleLanguage::new();
279        let expr = lang.create_expression("null").unwrap();
280        let ex = exchange_with_body("test");
281        let val = expr.evaluate(&ex).unwrap();
282        assert_eq!(val, Value::Null);
283    }
284
285    #[test]
286    fn test_predicate_null_is_false() {
287        let lang = SimpleLanguage::new();
288        let pred = lang.create_predicate("${header.missing}").unwrap();
289        let ex = exchange_with_body("test");
290        assert!(!pred.matches(&ex).unwrap());
291    }
292
293    #[test]
294    fn test_predicate_non_null_is_true() {
295        let lang = SimpleLanguage::new();
296        let pred = lang.create_predicate("${header.type}").unwrap();
297        let ex = exchange_with_header("type", "order");
298        assert!(pred.matches(&ex).unwrap());
299    }
300
301    #[test]
302    fn test_contains_not_found() {
303        let lang = SimpleLanguage::new();
304        let pred = lang.create_predicate("${body} contains 'xyz'").unwrap();
305        let ex = exchange_with_body("hello world");
306        assert!(!pred.matches(&ex).unwrap());
307    }
308
309    #[test]
310    fn test_less_than_or_equal() {
311        let lang = SimpleLanguage::new();
312        let pred = lang.create_predicate("${header.age} <= 18").unwrap();
313        let mut ex = Exchange::new(Message::default());
314        ex.input.set_header("age", Value::Number(18.into()));
315        assert!(pred.matches(&ex).unwrap());
316    }
317
318    // --- Mixed interpolation tests ---
319
320    #[test]
321    fn test_interpolated_text_with_header() {
322        // "Exchange #${header.CamelTimerCounter}" → "Exchange #42"
323        let lang = SimpleLanguage::new();
324        let expr = lang
325            .create_expression("Exchange #${header.CamelTimerCounter}")
326            .unwrap();
327        let ex = exchange_with_header("CamelTimerCounter", "42");
328        let val = expr.evaluate(&ex).unwrap();
329        assert_eq!(val, Value::String("Exchange #42".to_string()));
330    }
331
332    #[test]
333    fn test_interpolated_text_with_body() {
334        // "Got ${body}" → "Got hello"
335        let lang = SimpleLanguage::new();
336        let expr = lang.create_expression("Got ${body}").unwrap();
337        let ex = exchange_with_body("hello");
338        let val = expr.evaluate(&ex).unwrap();
339        assert_eq!(val, Value::String("Got hello".to_string()));
340    }
341
342    #[test]
343    fn test_interpolated_multiple_expressions() {
344        // "Transformed: ${body} (source=${header.source})" → real values
345        let lang = SimpleLanguage::new();
346        let expr = lang
347            .create_expression("Transformed: ${body} (source=${header.source})")
348            .unwrap();
349        let mut msg = Message::new("data");
350        msg.set_header("source", Value::String("kafka".to_string()));
351        let ex = Exchange::new(msg);
352        let val = expr.evaluate(&ex).unwrap();
353        assert_eq!(
354            val,
355            Value::String("Transformed: data (source=kafka)".to_string())
356        );
357    }
358
359    #[test]
360    fn test_interpolated_missing_header_becomes_empty() {
361        // Missing header in interpolated string yields empty string for that slot
362        let lang = SimpleLanguage::new();
363        let expr = lang
364            .create_expression("prefix-${header.missing}-suffix")
365            .unwrap();
366        let ex = exchange_with_body("x");
367        let val = expr.evaluate(&ex).unwrap();
368        assert_eq!(val, Value::String("prefix--suffix".to_string()));
369    }
370
371    #[test]
372    fn test_interpolated_text_only_no_expressions() {
373        // Plain text with no ${...} — treated as a literal (no interpolation needed,
374        // but must still work without error)
375        let lang = SimpleLanguage::new();
376        let expr = lang.create_expression("Hello World").unwrap();
377        let ex = exchange_with_body("x");
378        let val = expr.evaluate(&ex).unwrap();
379        assert_eq!(val, Value::String("Hello World".to_string()));
380    }
381
382    #[test]
383    fn test_interpolated_unclosed_brace_treated_as_literal() {
384        // An unclosed `${` has no matching `}` — the remainder is treated as
385        // plain literal text rather than causing a parse error.
386        let lang = SimpleLanguage::new();
387        let expr = lang.create_expression("Got ${body").unwrap();
388        let ex = exchange_with_body("hello");
389        let val = expr.evaluate(&ex).unwrap();
390        assert_eq!(val, Value::String("Got ${body".to_string()));
391    }
392
393    #[test]
394    fn test_operator_inside_string_literal_not_split() {
395        // The string literal 'a>=b' contains '>=' but must NOT be parsed as a
396        // BinOp split — the whole thing is a StringLit atom.
397        let lang = SimpleLanguage::new();
398        let result = lang.create_expression("'a>=b'");
399        let val = result
400            .unwrap()
401            .evaluate(&Exchange::new(Message::default()))
402            .unwrap();
403        assert_eq!(
404            val,
405            Value::String("a>=b".to_string()),
406            "string literal 'a>=b' should be parsed as a plain string, not split on >="
407        );
408    }
409
410    #[test]
411    fn test_header_eq_string_literal_containing_operator() {
412        // ${header.x} == 'a>=b' — the operator inside the RHS string must not
413        // cause the parser to split the LHS at the wrong position.
414        let lang = SimpleLanguage::new();
415        let pred = lang.create_predicate("${header.x} == 'a>=b'").unwrap();
416        let ex = exchange_with_header("x", "a>=b");
417        assert!(
418            pred.matches(&ex).unwrap(),
419            "predicate should match when header equals 'a>=b'"
420        );
421    }
422
423    #[test]
424    fn test_body_empty_is_null() {
425        let lang = SimpleLanguage::new();
426        let expr = lang.create_expression("${body}").unwrap();
427        let ex = Exchange::new(Message::default());
428        let val = expr.evaluate(&ex).unwrap();
429        assert_eq!(val, Value::Null);
430    }
431
432    #[test]
433    fn test_body_empty_not_null_is_false() {
434        let lang = SimpleLanguage::new();
435        let pred = lang.create_predicate("${body} != null").unwrap();
436        let ex = Exchange::new(Message::default());
437        assert!(!pred.matches(&ex).unwrap());
438    }
439
440    #[test]
441    fn test_body_empty_predicate_is_false() {
442        let lang = SimpleLanguage::new();
443        let pred = lang.create_predicate("${body}").unwrap();
444        let ex = Exchange::new(Message::default());
445        assert!(!pred.matches(&ex).unwrap());
446    }
447
448    #[test]
449    fn test_logical_and_true_true() {
450        let lang = SimpleLanguage::new();
451        let pred = lang
452            .create_predicate("${header.a} == '1' && ${header.b} == '2'")
453            .unwrap();
454        let mut msg = Message::default();
455        msg.set_header("a", Value::String("1".to_string()));
456        msg.set_header("b", Value::String("2".to_string()));
457        let ex = Exchange::new(msg);
458        assert!(pred.matches(&ex).unwrap());
459    }
460
461    #[test]
462    fn test_logical_and_true_false() {
463        let lang = SimpleLanguage::new();
464        let pred = lang
465            .create_predicate("${header.a} == '1' && ${header.b} == '2'")
466            .unwrap();
467        let mut msg = Message::default();
468        msg.set_header("a", Value::String("1".to_string()));
469        msg.set_header("b", Value::String("99".to_string()));
470        let ex = Exchange::new(msg);
471        assert!(!pred.matches(&ex).unwrap());
472    }
473
474    #[test]
475    fn test_logical_or_false_true() {
476        let lang = SimpleLanguage::new();
477        let pred = lang
478            .create_predicate("${header.a} == '1' || ${header.b} == '2'")
479            .unwrap();
480        let mut msg = Message::default();
481        msg.set_header("a", Value::String("0".to_string()));
482        msg.set_header("b", Value::String("2".to_string()));
483        let ex = Exchange::new(msg);
484        assert!(pred.matches(&ex).unwrap());
485    }
486
487    #[test]
488    fn test_logical_or_false_false() {
489        let lang = SimpleLanguage::new();
490        let pred = lang
491            .create_predicate("${header.a} == '1' || ${header.b} == '2'")
492            .unwrap();
493        let mut msg = Message::default();
494        msg.set_header("a", Value::String("0".to_string()));
495        msg.set_header("b", Value::String("0".to_string()));
496        let ex = Exchange::new(msg);
497        assert!(!pred.matches(&ex).unwrap());
498    }
499
500    #[test]
501    fn test_logical_and_precedence_over_or() {
502        let lang = SimpleLanguage::new();
503        let pred = lang
504            .create_predicate("${header.a} == '1' || ${header.b} == '2' && ${header.c} == '3'")
505            .unwrap();
506        let mut msg = Message::default();
507        msg.set_header("a", Value::String("1".to_string()));
508        msg.set_header("b", Value::String("2".to_string()));
509        msg.set_header("c", Value::String("999".to_string()));
510        let ex = Exchange::new(msg);
511        assert!(pred.matches(&ex).unwrap());
512    }
513
514    #[test]
515    fn test_logical_and_short_circuit() {
516        let lang = SimpleLanguage::new();
517        let pred = lang
518            .create_predicate("${header.a} == 'x' && ${header.nonexistent} > 999")
519            .unwrap();
520        let mut msg = Message::default();
521        msg.set_header("a", Value::String("not-x".to_string()));
522        let ex = Exchange::new(msg);
523        assert!(!pred.matches(&ex).unwrap());
524    }
525
526    #[test]
527    fn test_logical_or_short_circuit() {
528        let lang = SimpleLanguage::new();
529        let pred = lang
530            .create_predicate("${header.a} == '1' || ${header.nonexistent} > 999")
531            .unwrap();
532        let mut msg = Message::default();
533        msg.set_header("a", Value::String("1".to_string()));
534        let ex = Exchange::new(msg);
535        assert!(pred.matches(&ex).unwrap());
536    }
537
538    #[test]
539    fn test_compound_filter_body_not_null_and_body_not_empty() {
540        let lang = SimpleLanguage::new();
541        let pred = lang
542            .create_predicate("${body} != null && ${body} != ''")
543            .unwrap();
544        let ex = exchange_with_body("hello");
545        assert!(pred.matches(&ex).unwrap());
546
547        let ex_empty = Exchange::new(Message::default());
548        assert!(!pred.matches(&ex_empty).unwrap());
549    }
550
551    #[test]
552    fn test_header_with_gt_in_key() {
553        let lang = SimpleLanguage::new();
554        let mut msg = Message::default();
555        msg.set_header("a>b", Value::String("found".to_string()));
556        let pred = lang.create_predicate("${header.a>b} == 'found'").unwrap();
557        let ex = Exchange::new(msg);
558        assert!(pred.matches(&ex).unwrap());
559    }
560
561    #[test]
562    fn test_double_quoted_string_with_operator() {
563        let lang = SimpleLanguage::new();
564        let expr = lang.create_expression("\"a >= b\"").unwrap();
565        let ex = exchange_with_body("test");
566        let val = expr.evaluate(&ex).unwrap();
567        assert_eq!(val, Value::String("a >= b".to_string()));
568    }
569
570    #[test]
571    fn test_bool_literal_true() {
572        let lang = SimpleLanguage::new();
573        let expr = lang.create_expression("true").unwrap();
574        let ex = exchange_with_body("test");
575        let val = expr.evaluate(&ex).unwrap();
576        assert_eq!(val, Value::Bool(true));
577    }
578
579    #[test]
580    fn test_bool_literal_false() {
581        let lang = SimpleLanguage::new();
582        let expr = lang.create_expression("false").unwrap();
583        let ex = exchange_with_body("test");
584        let val = expr.evaluate(&ex).unwrap();
585        assert_eq!(val, Value::Bool(false));
586    }
587
588    #[test]
589    fn test_bool_literal_in_comparison() {
590        use camel_language_api::Body;
591
592        let lang = SimpleLanguage::new();
593        let mut ex = Exchange::default();
594        ex.input.body = Body::Json(serde_json::json!({"active": true}));
595        let pred = lang.create_predicate("${body.active} == true").unwrap();
596        assert!(pred.matches(&ex).unwrap());
597    }
598
599    #[test]
600    fn test_null_gt_number_is_false() {
601        let lang = SimpleLanguage::new();
602        let pred = lang.create_predicate("${header.missing} > 5").unwrap();
603        let ex = exchange_with_body("test");
604        assert!(!pred.matches(&ex).unwrap());
605    }
606
607    #[test]
608    fn test_null_lt_number_is_false() {
609        let lang = SimpleLanguage::new();
610        let pred = lang.create_predicate("${header.missing} < 10").unwrap();
611        let ex = exchange_with_body("test");
612        assert!(!pred.matches(&ex).unwrap());
613    }
614
615    #[test]
616    fn test_null_gte_number_is_false() {
617        let lang = SimpleLanguage::new();
618        let pred = lang.create_predicate("${header.missing} >= 0").unwrap();
619        let ex = exchange_with_body("test");
620        assert!(!pred.matches(&ex).unwrap());
621    }
622
623    #[test]
624    fn test_null_lte_number_is_false() {
625        let lang = SimpleLanguage::new();
626        let pred = lang.create_predicate("${header.missing} <= 100").unwrap();
627        let ex = exchange_with_body("test");
628        assert!(!pred.matches(&ex).unwrap());
629    }
630
631    #[test]
632    fn test_number_gt_null_is_false() {
633        let lang = SimpleLanguage::new();
634        let pred = lang.create_predicate("5 > ${header.missing}").unwrap();
635        let ex = exchange_with_body("test");
636        assert!(!pred.matches(&ex).unwrap());
637    }
638
639    #[test]
640    fn test_true_or_false() {
641        let lang = SimpleLanguage::new();
642        let pred = lang.create_predicate("true || false").unwrap();
643        let ex = exchange_with_body("test");
644        assert!(pred.matches(&ex).unwrap());
645    }
646
647    #[test]
648    fn test_false_and_true() {
649        let lang = SimpleLanguage::new();
650        let pred = lang.create_predicate("false && true").unwrap();
651        let ex = exchange_with_body("test");
652        assert!(!pred.matches(&ex).unwrap());
653    }
654
655    #[test]
656    fn test_contains_null_left_is_false() {
657        let lang = SimpleLanguage::new();
658        let pred = lang
659            .create_predicate("${header.missing} contains 'x'")
660            .unwrap();
661        let ex = exchange_with_body("test");
662        assert!(!pred.matches(&ex).unwrap());
663    }
664
665    #[test]
666    fn test_contains_null_right_is_false() {
667        let lang = SimpleLanguage::new();
668        let pred = lang.create_predicate("${body} contains null").unwrap();
669        let ex = exchange_with_body("test");
670        assert!(!pred.matches(&ex).unwrap());
671    }
672
673    #[test]
674    fn test_contains_without_spaces() {
675        let lang = SimpleLanguage::new();
676        let pred = lang.create_predicate("${body}contains'hello'").unwrap();
677        let ex = exchange_with_body("say hello world");
678        assert!(pred.matches(&ex).unwrap());
679    }
680
681    #[test]
682    fn test_non_finite_number_parse_error() {
683        let lang = SimpleLanguage::new();
684        // Very long digit string that overflows f64 to infinity
685        let result = lang.create_expression(
686            "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999",
687        );
688        assert!(result.is_err(), "non-finite number should be a parse error");
689    }
690
691    #[test]
692    fn test_interpolation_with_empty_body_still_produces_text() {
693        let lang = SimpleLanguage::new();
694        let expr = lang.create_expression("Got ${body}").unwrap();
695        let ex = Exchange::new(Message::default());
696        let val = expr.evaluate(&ex).unwrap();
697        assert_eq!(val, Value::String("Got ".to_string()));
698    }
699
700    #[test]
701    fn test_interpolation_with_empty_body_and_trailing_text() {
702        let lang = SimpleLanguage::new();
703        let expr = lang.create_expression("${body} tail").unwrap();
704        let ex = Exchange::new(Message::default());
705        let val = expr.evaluate(&ex).unwrap();
706        assert_eq!(val, Value::String(" tail".to_string()));
707    }
708}
709
710#[cfg(test)]
711mod body_field_parser_tests {
712    use crate::parser::{Expr, PathSegment, parse};
713
714    #[test]
715    fn parse_body_field_simple_key() {
716        let expr = parse("${body.name}").unwrap();
717        assert_eq!(
718            expr,
719            Expr::BodyField(vec![PathSegment::Key("name".to_string())])
720        );
721    }
722
723    #[test]
724    fn parse_body_field_nested() {
725        let expr = parse("${body.user.city}").unwrap();
726        assert_eq!(
727            expr,
728            Expr::BodyField(vec![
729                PathSegment::Key("user".to_string()),
730                PathSegment::Key("city".to_string()),
731            ])
732        );
733    }
734
735    #[test]
736    fn parse_body_field_array_index() {
737        let expr = parse("${body.items.0}").unwrap();
738        assert_eq!(
739            expr,
740            Expr::BodyField(vec![
741                PathSegment::Key("items".to_string()),
742                PathSegment::Index(0),
743            ])
744        );
745    }
746
747    #[test]
748    fn parse_body_field_array_nested() {
749        let expr = parse("${body.users.0.name}").unwrap();
750        assert_eq!(
751            expr,
752            Expr::BodyField(vec![
753                PathSegment::Key("users".to_string()),
754                PathSegment::Index(0),
755                PathSegment::Key("name".to_string()),
756            ])
757        );
758    }
759
760    #[test]
761    fn parse_body_field_empty_segment_error() {
762        let result = parse("${body.}");
763        assert!(result.is_err());
764    }
765
766    #[test]
767    fn parse_body_field_exact_still_works() {
768        // Regression: ${body} must still produce Expr::Body, not BodyField
769        let expr = parse("${body}").unwrap();
770        assert_eq!(expr, Expr::Body);
771    }
772
773    #[test]
774    fn parse_body_field_double_dots_error() {
775        // ${body..name} has an empty segment between the two dots
776        let result = parse("${body..name}");
777        assert!(result.is_err());
778    }
779
780    #[test]
781    fn parse_body_field_index_only() {
782        // ${body.0} — single index segment (e.g. body is a JSON array)
783        let expr = parse("${body.0}").unwrap();
784        assert_eq!(expr, Expr::BodyField(vec![PathSegment::Index(0)]));
785    }
786
787    #[test]
788    fn parse_body_field_leading_zero_is_key() {
789        // ${body.01} — leading zero means it's a string key, not an array index
790        let expr = parse("${body.01}").unwrap();
791        assert_eq!(
792            expr,
793            Expr::BodyField(vec![PathSegment::Key("01".to_string())])
794        );
795    }
796
797    #[test]
798    fn parse_language_delegate_jsonpath() {
799        let expr = parse("${jsonpath:$.store.book[0].title}").unwrap();
800        assert_eq!(
801            expr,
802            Expr::LanguageDelegate {
803                language: "jsonpath".to_string(),
804                expression: "$.store.book[0].title".to_string(),
805            }
806        );
807    }
808
809    #[test]
810    fn parse_language_delegate_xpath() {
811        let expr = parse("${xpath:/order/@id}").unwrap();
812        assert_eq!(
813            expr,
814            Expr::LanguageDelegate {
815                language: "xpath".to_string(),
816                expression: "/order/@id".to_string(),
817            }
818        );
819    }
820
821    #[test]
822    fn parse_language_delegate_allows_hyphen_and_digits() {
823        let expr = parse("${json-path2:$.a}").unwrap();
824        assert_eq!(
825            expr,
826            Expr::LanguageDelegate {
827                language: "json-path2".to_string(),
828                expression: "$.a".to_string(),
829            }
830        );
831    }
832
833    #[test]
834    fn parse_language_delegate_rejects_uppercase_language() {
835        let result = parse("${JsonPath:$.a}");
836        assert!(result.is_err());
837    }
838}
839
840#[cfg(test)]
841mod language_delegate_eval_tests {
842    use std::sync::Arc;
843
844    use crate::SimpleLanguage;
845    use camel_language_api::{Exchange, Expression, Language, LanguageError, Value};
846
847    struct MockExpression {
848        value: String,
849    }
850
851    impl Expression for MockExpression {
852        fn evaluate(&self, _exchange: &Exchange) -> Result<Value, LanguageError> {
853            Ok(Value::String(self.value.clone()))
854        }
855    }
856
857    struct MockLanguage;
858
859    impl Language for MockLanguage {
860        fn name(&self) -> &'static str {
861            "mock"
862        }
863
864        fn create_expression(&self, script: &str) -> Result<Box<dyn Expression>, LanguageError> {
865            Ok(Box::new(MockExpression {
866                value: format!("mock:{script}"),
867            }))
868        }
869
870        fn create_predicate(
871            &self,
872            _script: &str,
873        ) -> Result<Box<dyn camel_language_api::Predicate>, LanguageError> {
874            Err(LanguageError::NotSupported {
875                feature: "predicate".to_string(),
876                language: "mock".to_string(),
877            })
878        }
879    }
880
881    #[test]
882    fn evaluate_language_delegate_uses_resolver() {
883        let lang = SimpleLanguage::with_resolver(Arc::new(|name| {
884            if name == "mock" {
885                Some(Arc::new(MockLanguage))
886            } else {
887                None
888            }
889        }));
890        let expr = lang.create_expression("${mock:hello}").unwrap();
891        let ex = Exchange::default();
892        let out = expr.evaluate(&ex).unwrap();
893        assert_eq!(out, Value::String("mock:hello".to_string()));
894    }
895
896    #[test]
897    fn evaluate_language_delegate_without_resolver_errors() {
898        let lang = SimpleLanguage::new();
899        let expr = lang.create_expression("${mock:hello}").unwrap();
900        let ex = Exchange::default();
901        let err = expr.evaluate(&ex).expect_err("expected evaluation error");
902        assert!(format!("{err}").contains("No language resolver configured"));
903    }
904}
905
906#[cfg(test)]
907mod body_field_eval_tests {
908    use crate::SimpleLanguage;
909    use camel_language_api::Language;
910    use camel_language_api::{Body, Exchange, Value};
911    use serde_json::json;
912
913    fn eval(expr_str: &str, body: Body) -> Value {
914        let mut ex = Exchange::default();
915        ex.input.body = body;
916        let lang = SimpleLanguage::new();
917        lang.create_expression(expr_str)
918            .unwrap()
919            .evaluate(&ex)
920            .unwrap()
921    }
922
923    #[test]
924    fn body_field_simple_key() {
925        let result = eval("${body.name}", Body::Json(json!({"name": "Alice"})));
926        assert_eq!(result, json!("Alice"));
927    }
928
929    #[test]
930    fn body_field_number_value() {
931        let result = eval("${body.age}", Body::Json(json!({"age": 30})));
932        assert_eq!(result, json!(30));
933    }
934
935    #[test]
936    fn body_field_bool_value() {
937        let result = eval("${body.active}", Body::Json(json!({"active": true})));
938        assert_eq!(result, json!(true));
939    }
940
941    #[test]
942    fn body_field_nested() {
943        let result = eval(
944            "${body.user.city}",
945            Body::Json(json!({"user": {"city": "Madrid"}})),
946        );
947        assert_eq!(result, json!("Madrid"));
948    }
949
950    #[test]
951    fn body_field_array_index() {
952        let result = eval("${body.items.0}", Body::Json(json!({"items": ["a", "b"]})));
953        assert_eq!(result, json!("a"));
954    }
955
956    #[test]
957    fn body_field_array_nested() {
958        let result = eval(
959            "${body.users.0.name}",
960            Body::Json(json!({"users": [{"name": "Bob"}]})),
961        );
962        assert_eq!(result, json!("Bob"));
963    }
964
965    #[test]
966    fn body_field_missing_key_returns_null() {
967        let result = eval("${body.missing}", Body::Json(json!({"name": "Alice"})));
968        assert_eq!(result, Value::Null);
969    }
970
971    #[test]
972    fn body_field_missing_nested_returns_null() {
973        let result = eval("${body.a.b.c}", Body::Json(json!({"a": {"x": 1}})));
974        assert_eq!(result, Value::Null);
975    }
976
977    #[test]
978    fn body_field_out_of_bounds_index_returns_null() {
979        let result = eval("${body.items.5}", Body::Json(json!({"items": ["a"]})));
980        assert_eq!(result, Value::Null);
981    }
982
983    #[test]
984    fn body_field_non_json_body_returns_null() {
985        let result = eval(
986            "${body.name}",
987            Body::Text(r#"{"name":"Alice"}"#.to_string()),
988        );
989        assert_eq!(result, Value::Null);
990    }
991
992    #[test]
993    fn body_field_empty_body_returns_null() {
994        let result = eval("${body.name}", Body::Empty);
995        assert_eq!(result, Value::Null);
996    }
997
998    #[test]
999    fn body_field_in_interpolation() {
1000        let result = eval("Hello ${body.name}!", Body::Json(json!({"name": "Alice"})));
1001        assert_eq!(result, json!("Hello Alice!"));
1002    }
1003
1004    #[test]
1005    fn body_field_in_predicate_true() {
1006        let lang = SimpleLanguage::new();
1007        let mut ex = Exchange::default();
1008        ex.input.body = Body::Json(json!({"status": "active"}));
1009        let result = lang
1010            .create_expression("${body.status} == 'active'")
1011            .unwrap()
1012            .evaluate(&ex)
1013            .unwrap();
1014        assert_eq!(result, Value::Bool(true));
1015    }
1016
1017    #[test]
1018    fn body_field_in_predicate_false() {
1019        let lang = SimpleLanguage::new();
1020        let mut ex = Exchange::default();
1021        ex.input.body = Body::Json(json!({"status": "inactive"}));
1022        let result = lang
1023            .create_expression("${body.status} == 'active'")
1024            .unwrap()
1025            .evaluate(&ex)
1026            .unwrap();
1027        assert_eq!(result, Value::Bool(false));
1028    }
1029
1030    #[test]
1031    fn body_field_bytes_body_returns_null() {
1032        // Note: Using Body::Text here instead of Body::Bytes since bytes crate
1033        // is not available in test context. Both should behave the same for JSON field access.
1034        let result = eval(
1035            "${body.name}",
1036            Body::Text(r#"{"name":"Alice"}"#.to_string()),
1037        );
1038        assert_eq!(result, Value::Null);
1039    }
1040
1041    #[test]
1042    fn body_field_json_null_value_returns_null() {
1043        // key exists but its value is JSON null → returns Value::Null
1044        let result = eval(
1045            "${body.name}",
1046            Body::Json(serde_json::json!({"name": null})),
1047        );
1048        assert_eq!(result, Value::Null);
1049    }
1050
1051    #[test]
1052    fn body_field_numeric_predicate() {
1053        // JSON number 42.0 compares equal to the parsed number 42 from the
1054        // Simple Language expression, because both resolve to the same f64.
1055        let lang = SimpleLanguage::new();
1056        let mut ex = Exchange::default();
1057        ex.input.body = Body::Json(json!({"score": 42.0}));
1058        let result = lang
1059            .create_expression("${body.score} == 42")
1060            .unwrap()
1061            .evaluate(&ex)
1062            .unwrap();
1063        assert_eq!(result, Value::Bool(true));
1064    }
1065
1066    #[test]
1067    fn body_bytes_utf8_returns_string() {
1068        // Body::Bytes with valid UTF-8 content should be readable via ${body}
1069        let lang = SimpleLanguage::new();
1070        let mut ex = Exchange::default();
1071        ex.input.body = Body::from(b"hello from bytes".to_vec());
1072        let val = lang
1073            .create_expression("${body}")
1074            .unwrap()
1075            .evaluate(&ex)
1076            .unwrap();
1077        assert_eq!(val, Value::String("hello from bytes".to_string()));
1078    }
1079
1080    #[test]
1081    fn body_json_returns_serialized_string() {
1082        // Body::Json should be serialized to a JSON string when accessed via ${body}
1083        let lang = SimpleLanguage::new();
1084        let mut ex = Exchange::default();
1085        ex.input.body = Body::Json(json!({"msg": "world"}));
1086        let val = lang
1087            .create_expression("${body}")
1088            .unwrap()
1089            .evaluate(&ex)
1090            .unwrap();
1091        // The result should be the JSON serialization, not empty
1092        let s = match val {
1093            Value::String(s) => s,
1094            other => panic!("expected String, got {other:?}"),
1095        };
1096        assert!(!s.is_empty(), "${{body}} on Body::Json should not be empty");
1097        let parsed: serde_json::Value = serde_json::from_str(&s).unwrap();
1098        assert_eq!(parsed["msg"], "world");
1099    }
1100
1101    #[test]
1102    fn body_bytes_in_interpolation() {
1103        // Body::Bytes should work in interpolated expressions like "Received: ${body}"
1104        let lang = SimpleLanguage::new();
1105        let mut ex = Exchange::default();
1106        ex.input.body = Body::from(b"ping".to_vec());
1107        let val = lang
1108            .create_expression("Received: ${body}")
1109            .unwrap()
1110            .evaluate(&ex)
1111            .unwrap();
1112        assert_eq!(val, Value::String("Received: ping".to_string()));
1113    }
1114
1115    // --- XML→Json→Simple integration tests ---
1116    // These verify that JSON keys produced by xml_to_json() (@attr, #text, arrays)
1117    // are navigable via Simple language BodyField expressions.
1118
1119    #[test]
1120    fn body_field_xml_attr_key() {
1121        // XML: <order id="123"> → JSON: {"order": {"@id": "123"}}
1122        let lang = SimpleLanguage::new();
1123        let mut ex = Exchange::default();
1124        ex.input.body = Body::Json(json!({"order": {"@id": "123"}}));
1125        let val = lang
1126            .create_expression("${body.order.@id}")
1127            .unwrap()
1128            .evaluate(&ex)
1129            .unwrap();
1130        assert_eq!(val, json!("123"));
1131    }
1132
1133    #[test]
1134    fn body_field_xml_hash_text() {
1135        // XML: <status active="true">pending</status> → JSON: {"status": {"@active": "true", "#text": "pending"}}
1136        let lang = SimpleLanguage::new();
1137        let mut ex = Exchange::default();
1138        ex.input.body = Body::Json(json!({"status": {"@active": "true", "#text": "pending"}}));
1139        let val = lang
1140            .create_expression("${body.status.#text}")
1141            .unwrap()
1142            .evaluate(&ex)
1143            .unwrap();
1144        assert_eq!(val, json!("pending"));
1145    }
1146
1147    #[test]
1148    fn body_field_xml_array_items() {
1149        // XML: <order><item>coffee</item><item>tea</item></order> → JSON: {"order": {"item": ["coffee", "tea"]}}
1150        let lang = SimpleLanguage::new();
1151        let mut ex = Exchange::default();
1152        ex.input.body = Body::Json(json!({"order": {"item": ["coffee", "tea"]}}));
1153        let val0 = lang
1154            .create_expression("${body.order.item.0}")
1155            .unwrap()
1156            .evaluate(&ex)
1157            .unwrap();
1158        assert_eq!(val0, json!("coffee"));
1159        let val1 = lang
1160            .create_expression("${body.order.item.1}")
1161            .unwrap()
1162            .evaluate(&ex)
1163            .unwrap();
1164        assert_eq!(val1, json!("tea"));
1165    }
1166
1167    #[test]
1168    fn body_field_xml_array_with_attrs() {
1169        // XML: <root><item id="1">a</item><item id="2">b</item></root>
1170        // → JSON: {"root": {"item": [{"@id": "1", "#text": "a"}, {"@id": "2", "#text": "b"}]}}
1171        let lang = SimpleLanguage::new();
1172        let mut ex = Exchange::default();
1173        ex.input.body = Body::Json(
1174            json!({"root": {"item": [{"@id": "1", "#text": "a"}, {"@id": "2", "#text": "b"}]}}),
1175        );
1176        let val = lang
1177            .create_expression("${body.root.item.0.@id}")
1178            .unwrap()
1179            .evaluate(&ex)
1180            .unwrap();
1181        assert_eq!(val, json!("1"));
1182        let val_text = lang
1183            .create_expression("${body.root.item.1.#text}")
1184            .unwrap()
1185            .evaluate(&ex)
1186            .unwrap();
1187        assert_eq!(val_text, json!("b"));
1188    }
1189
1190    #[test]
1191    fn body_field_xml_predicate_on_attr() {
1192        // Predicate: ${body.order.@id} == '123'
1193        let lang = SimpleLanguage::new();
1194        let mut ex = Exchange::default();
1195        ex.input.body = Body::Json(json!({"order": {"@id": "123", "name": "test"}}));
1196        let pred = lang.create_predicate("${body.order.@id} == '123'").unwrap();
1197        assert!(pred.matches(&ex).unwrap());
1198    }
1199}
1200
1201#[cfg(test)]
1202mod public_api_tests {
1203    use super::SimpleLanguage;
1204    use camel_language_api::{Exchange, Expression, Language, LanguageError, Predicate, Value};
1205    use std::sync::Arc;
1206
1207    struct BoolExpression(bool);
1208
1209    impl Expression for BoolExpression {
1210        fn evaluate(&self, _exchange: &Exchange) -> Result<Value, LanguageError> {
1211            Ok(Value::Bool(self.0))
1212        }
1213    }
1214
1215    struct BoolLanguage;
1216
1217    impl Language for BoolLanguage {
1218        fn name(&self) -> &'static str {
1219            "boollang"
1220        }
1221
1222        fn create_expression(&self, script: &str) -> Result<Box<dyn Expression>, LanguageError> {
1223            Ok(Box::new(BoolExpression(script == "yes")))
1224        }
1225
1226        fn create_predicate(&self, _script: &str) -> Result<Box<dyn Predicate>, LanguageError> {
1227            Err(LanguageError::NotSupported {
1228                feature: "predicate".to_string(),
1229                language: "boollang".to_string(),
1230            })
1231        }
1232    }
1233
1234    #[test]
1235    fn language_name_and_default() {
1236        let lang = SimpleLanguage::default();
1237        assert_eq!(lang.name(), "simple");
1238    }
1239
1240    #[test]
1241    fn create_expression_and_predicate_propagate_parse_errors() {
1242        let lang = SimpleLanguage::new();
1243        assert!(lang.create_expression("${header.}").is_err());
1244        assert!(lang.create_predicate("${exchangeProperty.}").is_err());
1245    }
1246
1247    #[test]
1248    fn with_resolver_allows_delegate_expressions() {
1249        let resolver = Arc::new(|name: &str| {
1250            if name == "boollang" {
1251                Some(Arc::new(BoolLanguage) as Arc<dyn Language>)
1252            } else {
1253                None
1254            }
1255        });
1256        let lang = SimpleLanguage::with_resolver(resolver);
1257        let expr = lang.create_expression("${boollang:yes}").unwrap();
1258        let val = expr.evaluate(&Exchange::default()).unwrap();
1259        assert_eq!(val, Value::Bool(true));
1260    }
1261}