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