1mod evaluator;
2mod parser;
3
4use camel_language_api::{Exchange, Expression, Language, LanguageError, Predicate, Value};
5
6pub struct SimpleLanguage;
7
8struct SimpleExpression(parser::Expr);
9struct SimplePredicate(parser::Expr);
10
11impl Expression for SimpleExpression {
12 fn evaluate(&self, exchange: &Exchange) -> Result<Value, LanguageError> {
13 evaluator::evaluate(&self.0, exchange)
14 }
15}
16
17impl Predicate for SimplePredicate {
18 fn matches(&self, exchange: &Exchange) -> Result<bool, LanguageError> {
19 let val = evaluator::evaluate(&self.0, exchange)?;
20 Ok(match &val {
21 Value::Bool(b) => *b,
22 Value::Null => false,
23 _ => true,
24 })
25 }
26}
27
28impl Language for SimpleLanguage {
29 fn name(&self) -> &'static str {
30 "simple"
31 }
32
33 fn create_expression(&self, script: &str) -> Result<Box<dyn Expression>, LanguageError> {
34 let ast = parser::parse(script)?;
35 Ok(Box::new(SimpleExpression(ast)))
36 }
37
38 fn create_predicate(&self, script: &str) -> Result<Box<dyn Predicate>, LanguageError> {
39 let ast = parser::parse(script)?;
40 Ok(Box::new(SimplePredicate(ast)))
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::SimpleLanguage;
47 use camel_language_api::Language;
48 use camel_language_api::{Exchange, Message, Value};
49
50 fn exchange_with_header(key: &str, val: &str) -> Exchange {
51 let mut msg = Message::default();
52 msg.set_header(key, Value::String(val.to_string()));
53 Exchange::new(msg)
54 }
55
56 fn exchange_with_body(body: &str) -> Exchange {
57 Exchange::new(Message::new(body))
58 }
59
60 #[test]
61 fn test_header_equals_string() {
62 let lang = SimpleLanguage;
63 let pred = lang.create_predicate("${header.type} == 'order'").unwrap();
64 let ex = exchange_with_header("type", "order");
65 assert!(pred.matches(&ex).unwrap());
66 }
67
68 #[test]
69 fn test_header_not_equals() {
70 let lang = SimpleLanguage;
71 let pred = lang.create_predicate("${header.type} != 'order'").unwrap();
72 let ex = exchange_with_header("type", "invoice");
73 assert!(pred.matches(&ex).unwrap());
74 }
75
76 #[test]
77 fn test_body_contains() {
78 let lang = SimpleLanguage;
79 let pred = lang.create_predicate("${body} contains 'hello'").unwrap();
80 let ex = exchange_with_body("say hello world");
81 assert!(pred.matches(&ex).unwrap());
82 }
83
84 #[test]
85 fn test_header_null_check() {
86 let lang = SimpleLanguage;
87 let pred = lang.create_predicate("${header.missing} == null").unwrap();
88 let ex = exchange_with_body("anything");
89 assert!(pred.matches(&ex).unwrap());
90 }
91
92 #[test]
93 fn test_header_not_null() {
94 let lang = SimpleLanguage;
95 let pred = lang.create_predicate("${header.type} != null").unwrap();
96 let ex = exchange_with_header("type", "order");
97 assert!(pred.matches(&ex).unwrap());
98 }
99
100 #[test]
101 fn test_expression_header_value() {
102 let lang = SimpleLanguage;
103 let expr = lang.create_expression("${header.type}").unwrap();
104 let ex = exchange_with_header("type", "order");
105 let val = expr.evaluate(&ex).unwrap();
106 assert_eq!(val, Value::String("order".to_string()));
107 }
108
109 #[test]
110 fn test_expression_body() {
111 let lang = SimpleLanguage;
112 let expr = lang.create_expression("${body}").unwrap();
113 let ex = exchange_with_body("hello");
114 let val = expr.evaluate(&ex).unwrap();
115 assert_eq!(val, Value::String("hello".to_string()));
116 }
117
118 #[test]
119 fn test_numeric_comparison() {
120 let lang = SimpleLanguage;
121 let pred = lang.create_predicate("${header.age} > 18").unwrap();
122 let mut ex = Exchange::new(Message::default());
123 ex.input.set_header("age", Value::Number(25.into()));
124 assert!(pred.matches(&ex).unwrap());
125 }
126
127 #[test]
130 fn test_empty_body() {
131 let lang = SimpleLanguage;
132 let expr = lang.create_expression("${body}").unwrap();
133 let ex = Exchange::new(Message::default());
134 let val = expr.evaluate(&ex).unwrap();
135 assert_eq!(val, Value::Null);
136 }
137
138 #[test]
139 fn test_parse_error_unrecognized_token() {
140 let lang = SimpleLanguage;
143 let result = lang.create_expression("${unknown}");
144 assert!(result.is_err(), "unknown token should be a parse error");
145 }
146
147 #[test]
148 fn test_empty_header_key_is_parse_error() {
149 let lang = SimpleLanguage;
150 let result = lang.create_expression("${header.}");
151 let err = result.err().expect("should be a parse error");
152 let err = format!("{err}");
153 assert!(
154 err.contains("empty"),
155 "error should mention empty key, got: {err}"
156 );
157 }
158
159 #[test]
160 fn test_empty_exchange_property_key_is_parse_error() {
161 let lang = SimpleLanguage;
162 let result = lang.create_expression("${exchangeProperty.}");
163 let err = result.err().expect("should be a parse error");
164 let err = format!("{err}");
165 assert!(
166 err.contains("empty"),
167 "error should mention empty key, got: {err}"
168 );
169 }
170
171 #[test]
172 fn test_missing_header_returns_null() {
173 let lang = SimpleLanguage;
174 let expr = lang.create_expression("${header.nonexistent}").unwrap();
175 let ex = exchange_with_body("anything");
176 let val = expr.evaluate(&ex).unwrap();
177 assert_eq!(val, Value::Null);
178 }
179
180 #[test]
181 fn test_exchange_property_expression() {
182 let lang = SimpleLanguage;
183 let expr = lang
184 .create_expression("${exchangeProperty.myProp}")
185 .unwrap();
186 let mut ex = exchange_with_body("test");
187 ex.set_property("myProp".to_string(), Value::String("propVal".to_string()));
188 let val = expr.evaluate(&ex).unwrap();
189 assert_eq!(val, Value::String("propVal".to_string()));
190 }
191
192 #[test]
193 fn test_missing_property_returns_null() {
194 let lang = SimpleLanguage;
195 let expr = lang
196 .create_expression("${exchangeProperty.missing}")
197 .unwrap();
198 let ex = exchange_with_body("test");
199 let val = expr.evaluate(&ex).unwrap();
200 assert_eq!(val, Value::Null);
201 }
202
203 #[test]
204 fn test_string_literal_expression() {
205 let lang = SimpleLanguage;
206 let expr = lang.create_expression("'hello'").unwrap();
207 let ex = exchange_with_body("test");
208 let val = expr.evaluate(&ex).unwrap();
209 assert_eq!(val, Value::String("hello".to_string()));
210 }
211
212 #[test]
213 fn test_double_quoted_literal_unescapes_newline() {
214 let lang = SimpleLanguage;
215 let expr = lang.create_expression("\"line1\\nline2\"").unwrap();
216 let ex = exchange_with_body("test");
217 let val = expr.evaluate(&ex).unwrap();
218 assert_eq!(val, Value::String("line1\nline2".to_string()));
219 }
220
221 #[test]
222 fn test_double_quoted_literal_unescapes_tab_and_quote() {
223 let lang = SimpleLanguage;
224 let expr = lang.create_expression("\"col1\\t\\\"quoted\\\"\"").unwrap();
225 let ex = exchange_with_body("test");
226 let val = expr.evaluate(&ex).unwrap();
227 assert_eq!(val, Value::String("col1\t\"quoted\"".to_string()));
228 }
229
230 #[test]
231 fn test_double_quoted_literal_unescapes_backspace_formfeed_and_slash() {
232 let lang = SimpleLanguage;
233 let expr = lang.create_expression("\"a\\bb\\fc\\/d\"").unwrap();
234 let ex = exchange_with_body("test");
235 let val = expr.evaluate(&ex).unwrap();
236 assert_eq!(val, Value::String("a\u{0008}b\u{000C}c/d".to_string()));
237 }
238
239 #[test]
240 fn test_null_expression() {
241 let lang = SimpleLanguage;
242 let expr = lang.create_expression("null").unwrap();
243 let ex = exchange_with_body("test");
244 let val = expr.evaluate(&ex).unwrap();
245 assert_eq!(val, Value::Null);
246 }
247
248 #[test]
249 fn test_predicate_null_is_false() {
250 let lang = SimpleLanguage;
251 let pred = lang.create_predicate("${header.missing}").unwrap();
252 let ex = exchange_with_body("test");
253 assert!(!pred.matches(&ex).unwrap());
254 }
255
256 #[test]
257 fn test_predicate_non_null_is_true() {
258 let lang = SimpleLanguage;
259 let pred = lang.create_predicate("${header.type}").unwrap();
260 let ex = exchange_with_header("type", "order");
261 assert!(pred.matches(&ex).unwrap());
262 }
263
264 #[test]
265 fn test_contains_not_found() {
266 let lang = SimpleLanguage;
267 let pred = lang.create_predicate("${body} contains 'xyz'").unwrap();
268 let ex = exchange_with_body("hello world");
269 assert!(!pred.matches(&ex).unwrap());
270 }
271
272 #[test]
273 fn test_less_than_or_equal() {
274 let lang = SimpleLanguage;
275 let pred = lang.create_predicate("${header.age} <= 18").unwrap();
276 let mut ex = Exchange::new(Message::default());
277 ex.input.set_header("age", Value::Number(18.into()));
278 assert!(pred.matches(&ex).unwrap());
279 }
280
281 #[test]
284 fn test_interpolated_text_with_header() {
285 let lang = SimpleLanguage;
287 let expr = lang
288 .create_expression("Exchange #${header.CamelTimerCounter}")
289 .unwrap();
290 let ex = exchange_with_header("CamelTimerCounter", "42");
291 let val = expr.evaluate(&ex).unwrap();
292 assert_eq!(val, Value::String("Exchange #42".to_string()));
293 }
294
295 #[test]
296 fn test_interpolated_text_with_body() {
297 let lang = SimpleLanguage;
299 let expr = lang.create_expression("Got ${body}").unwrap();
300 let ex = exchange_with_body("hello");
301 let val = expr.evaluate(&ex).unwrap();
302 assert_eq!(val, Value::String("Got hello".to_string()));
303 }
304
305 #[test]
306 fn test_interpolated_multiple_expressions() {
307 let lang = SimpleLanguage;
309 let expr = lang
310 .create_expression("Transformed: ${body} (source=${header.source})")
311 .unwrap();
312 let mut msg = Message::new("data");
313 msg.set_header("source", Value::String("kafka".to_string()));
314 let ex = Exchange::new(msg);
315 let val = expr.evaluate(&ex).unwrap();
316 assert_eq!(
317 val,
318 Value::String("Transformed: data (source=kafka)".to_string())
319 );
320 }
321
322 #[test]
323 fn test_interpolated_missing_header_becomes_empty() {
324 let lang = SimpleLanguage;
326 let expr = lang
327 .create_expression("prefix-${header.missing}-suffix")
328 .unwrap();
329 let ex = exchange_with_body("x");
330 let val = expr.evaluate(&ex).unwrap();
331 assert_eq!(val, Value::String("prefix--suffix".to_string()));
332 }
333
334 #[test]
335 fn test_interpolated_text_only_no_expressions() {
336 let lang = SimpleLanguage;
339 let expr = lang.create_expression("Hello World").unwrap();
340 let ex = exchange_with_body("x");
341 let val = expr.evaluate(&ex).unwrap();
342 assert_eq!(val, Value::String("Hello World".to_string()));
343 }
344
345 #[test]
346 fn test_interpolated_unclosed_brace_treated_as_literal() {
347 let lang = SimpleLanguage;
350 let expr = lang.create_expression("Got ${body").unwrap();
351 let ex = exchange_with_body("hello");
352 let val = expr.evaluate(&ex).unwrap();
353 assert_eq!(val, Value::String("Got ${body".to_string()));
354 }
355
356 #[test]
357 fn test_operator_inside_string_literal_not_split() {
358 let lang = SimpleLanguage;
361 let result = lang.create_expression("'a>=b'");
362 let val = result
363 .unwrap()
364 .evaluate(&Exchange::new(Message::default()))
365 .unwrap();
366 assert_eq!(
367 val,
368 Value::String("a>=b".to_string()),
369 "string literal 'a>=b' should be parsed as a plain string, not split on >="
370 );
371 }
372
373 #[test]
374 fn test_header_eq_string_literal_containing_operator() {
375 let lang = SimpleLanguage;
378 let pred = lang.create_predicate("${header.x} == 'a>=b'").unwrap();
379 let ex = exchange_with_header("x", "a>=b");
380 assert!(
381 pred.matches(&ex).unwrap(),
382 "predicate should match when header equals 'a>=b'"
383 );
384 }
385
386 #[test]
387 fn test_body_empty_is_null() {
388 let lang = SimpleLanguage;
389 let expr = lang.create_expression("${body}").unwrap();
390 let ex = Exchange::new(Message::default());
391 let val = expr.evaluate(&ex).unwrap();
392 assert_eq!(val, Value::Null);
393 }
394
395 #[test]
396 fn test_body_empty_not_null_is_false() {
397 let lang = SimpleLanguage;
398 let pred = lang.create_predicate("${body} != null").unwrap();
399 let ex = Exchange::new(Message::default());
400 assert!(!pred.matches(&ex).unwrap());
401 }
402
403 #[test]
404 fn test_body_empty_predicate_is_false() {
405 let lang = SimpleLanguage;
406 let pred = lang.create_predicate("${body}").unwrap();
407 let ex = Exchange::new(Message::default());
408 assert!(!pred.matches(&ex).unwrap());
409 }
410
411 #[test]
412 fn test_logical_and_true_true() {
413 let lang = SimpleLanguage;
414 let pred = lang
415 .create_predicate("${header.a} == '1' && ${header.b} == '2'")
416 .unwrap();
417 let mut msg = Message::default();
418 msg.set_header("a", Value::String("1".to_string()));
419 msg.set_header("b", Value::String("2".to_string()));
420 let ex = Exchange::new(msg);
421 assert!(pred.matches(&ex).unwrap());
422 }
423
424 #[test]
425 fn test_logical_and_true_false() {
426 let lang = SimpleLanguage;
427 let pred = lang
428 .create_predicate("${header.a} == '1' && ${header.b} == '2'")
429 .unwrap();
430 let mut msg = Message::default();
431 msg.set_header("a", Value::String("1".to_string()));
432 msg.set_header("b", Value::String("99".to_string()));
433 let ex = Exchange::new(msg);
434 assert!(!pred.matches(&ex).unwrap());
435 }
436
437 #[test]
438 fn test_logical_or_false_true() {
439 let lang = SimpleLanguage;
440 let pred = lang
441 .create_predicate("${header.a} == '1' || ${header.b} == '2'")
442 .unwrap();
443 let mut msg = Message::default();
444 msg.set_header("a", Value::String("0".to_string()));
445 msg.set_header("b", Value::String("2".to_string()));
446 let ex = Exchange::new(msg);
447 assert!(pred.matches(&ex).unwrap());
448 }
449
450 #[test]
451 fn test_logical_or_false_false() {
452 let lang = SimpleLanguage;
453 let pred = lang
454 .create_predicate("${header.a} == '1' || ${header.b} == '2'")
455 .unwrap();
456 let mut msg = Message::default();
457 msg.set_header("a", Value::String("0".to_string()));
458 msg.set_header("b", Value::String("0".to_string()));
459 let ex = Exchange::new(msg);
460 assert!(!pred.matches(&ex).unwrap());
461 }
462
463 #[test]
464 fn test_logical_and_precedence_over_or() {
465 let lang = SimpleLanguage;
466 let pred = lang
467 .create_predicate("${header.a} == '1' || ${header.b} == '2' && ${header.c} == '3'")
468 .unwrap();
469 let mut msg = Message::default();
470 msg.set_header("a", Value::String("1".to_string()));
471 msg.set_header("b", Value::String("2".to_string()));
472 msg.set_header("c", Value::String("999".to_string()));
473 let ex = Exchange::new(msg);
474 assert!(pred.matches(&ex).unwrap());
475 }
476
477 #[test]
478 fn test_logical_and_short_circuit() {
479 let lang = SimpleLanguage;
480 let pred = lang
481 .create_predicate("${header.a} == 'x' && ${header.nonexistent} > 999")
482 .unwrap();
483 let mut msg = Message::default();
484 msg.set_header("a", Value::String("not-x".to_string()));
485 let ex = Exchange::new(msg);
486 assert!(!pred.matches(&ex).unwrap());
487 }
488
489 #[test]
490 fn test_logical_or_short_circuit() {
491 let lang = SimpleLanguage;
492 let pred = lang
493 .create_predicate("${header.a} == '1' || ${header.nonexistent} > 999")
494 .unwrap();
495 let mut msg = Message::default();
496 msg.set_header("a", Value::String("1".to_string()));
497 let ex = Exchange::new(msg);
498 assert!(pred.matches(&ex).unwrap());
499 }
500
501 #[test]
502 fn test_compound_filter_body_not_null_and_body_not_empty() {
503 let lang = SimpleLanguage;
504 let pred = lang
505 .create_predicate("${body} != null && ${body} != ''")
506 .unwrap();
507 let ex = exchange_with_body("hello");
508 assert!(pred.matches(&ex).unwrap());
509
510 let ex_empty = Exchange::new(Message::default());
511 assert!(!pred.matches(&ex_empty).unwrap());
512 }
513
514 #[test]
515 fn test_header_with_gt_in_key() {
516 let lang = SimpleLanguage;
517 let mut msg = Message::default();
518 msg.set_header("a>b", Value::String("found".to_string()));
519 let pred = lang.create_predicate("${header.a>b} == 'found'").unwrap();
520 let ex = Exchange::new(msg);
521 assert!(pred.matches(&ex).unwrap());
522 }
523
524 #[test]
525 fn test_double_quoted_string_with_operator() {
526 let lang = SimpleLanguage;
527 let expr = lang.create_expression("\"a >= b\"").unwrap();
528 let ex = exchange_with_body("test");
529 let val = expr.evaluate(&ex).unwrap();
530 assert_eq!(val, Value::String("a >= b".to_string()));
531 }
532
533 #[test]
534 fn test_bool_literal_true() {
535 let lang = SimpleLanguage;
536 let expr = lang.create_expression("true").unwrap();
537 let ex = exchange_with_body("test");
538 let val = expr.evaluate(&ex).unwrap();
539 assert_eq!(val, Value::Bool(true));
540 }
541
542 #[test]
543 fn test_bool_literal_false() {
544 let lang = SimpleLanguage;
545 let expr = lang.create_expression("false").unwrap();
546 let ex = exchange_with_body("test");
547 let val = expr.evaluate(&ex).unwrap();
548 assert_eq!(val, Value::Bool(false));
549 }
550
551 #[test]
552 fn test_bool_literal_in_comparison() {
553 use camel_language_api::Body;
554
555 let lang = SimpleLanguage;
556 let mut ex = Exchange::default();
557 ex.input.body = Body::Json(serde_json::json!({"active": true}));
558 let pred = lang.create_predicate("${body.active} == true").unwrap();
559 assert!(pred.matches(&ex).unwrap());
560 }
561
562 #[test]
563 fn test_null_gt_number_is_false() {
564 let lang = SimpleLanguage;
565 let pred = lang.create_predicate("${header.missing} > 5").unwrap();
566 let ex = exchange_with_body("test");
567 assert!(!pred.matches(&ex).unwrap());
568 }
569
570 #[test]
571 fn test_null_lt_number_is_false() {
572 let lang = SimpleLanguage;
573 let pred = lang.create_predicate("${header.missing} < 10").unwrap();
574 let ex = exchange_with_body("test");
575 assert!(!pred.matches(&ex).unwrap());
576 }
577
578 #[test]
579 fn test_null_gte_number_is_false() {
580 let lang = SimpleLanguage;
581 let pred = lang.create_predicate("${header.missing} >= 0").unwrap();
582 let ex = exchange_with_body("test");
583 assert!(!pred.matches(&ex).unwrap());
584 }
585
586 #[test]
587 fn test_null_lte_number_is_false() {
588 let lang = SimpleLanguage;
589 let pred = lang.create_predicate("${header.missing} <= 100").unwrap();
590 let ex = exchange_with_body("test");
591 assert!(!pred.matches(&ex).unwrap());
592 }
593
594 #[test]
595 fn test_number_gt_null_is_false() {
596 let lang = SimpleLanguage;
597 let pred = lang.create_predicate("5 > ${header.missing}").unwrap();
598 let ex = exchange_with_body("test");
599 assert!(!pred.matches(&ex).unwrap());
600 }
601
602 #[test]
603 fn test_true_or_false() {
604 let lang = SimpleLanguage;
605 let pred = lang.create_predicate("true || false").unwrap();
606 let ex = exchange_with_body("test");
607 assert!(pred.matches(&ex).unwrap());
608 }
609
610 #[test]
611 fn test_false_and_true() {
612 let lang = SimpleLanguage;
613 let pred = lang.create_predicate("false && true").unwrap();
614 let ex = exchange_with_body("test");
615 assert!(!pred.matches(&ex).unwrap());
616 }
617
618 #[test]
619 fn test_contains_null_left_is_false() {
620 let lang = SimpleLanguage;
621 let pred = lang.create_predicate("${header.missing} contains 'x'").unwrap();
622 let ex = exchange_with_body("test");
623 assert!(!pred.matches(&ex).unwrap());
624 }
625
626 #[test]
627 fn test_contains_null_right_is_false() {
628 let lang = SimpleLanguage;
629 let pred = lang.create_predicate("${body} contains null").unwrap();
630 let ex = exchange_with_body("test");
631 assert!(!pred.matches(&ex).unwrap());
632 }
633
634 #[test]
635 fn test_contains_without_spaces() {
636 let lang = SimpleLanguage;
637 let pred = lang.create_predicate("${body}contains'hello'").unwrap();
638 let ex = exchange_with_body("say hello world");
639 assert!(pred.matches(&ex).unwrap());
640 }
641
642 #[test]
643 fn test_non_finite_number_parse_error() {
644 let lang = SimpleLanguage;
645 let result = lang.create_expression(
647 "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999",
648 );
649 assert!(result.is_err(), "non-finite number should be a parse error");
650 }
651
652 #[test]
653 fn test_interpolation_with_empty_body_still_produces_text() {
654 let lang = SimpleLanguage;
655 let expr = lang.create_expression("Got ${body}").unwrap();
656 let ex = Exchange::new(Message::default());
657 let val = expr.evaluate(&ex).unwrap();
658 assert_eq!(val, Value::String("Got ".to_string()));
659 }
660
661 #[test]
662 fn test_interpolation_with_empty_body_and_trailing_text() {
663 let lang = SimpleLanguage;
664 let expr = lang.create_expression("${body} tail").unwrap();
665 let ex = Exchange::new(Message::default());
666 let val = expr.evaluate(&ex).unwrap();
667 assert_eq!(val, Value::String(" tail".to_string()));
668 }
669}
670
671#[cfg(test)]
672mod body_field_parser_tests {
673 use crate::parser::{Expr, PathSegment, parse};
674
675 #[test]
676 fn parse_body_field_simple_key() {
677 let expr = parse("${body.name}").unwrap();
678 assert_eq!(
679 expr,
680 Expr::BodyField(vec![PathSegment::Key("name".to_string())])
681 );
682 }
683
684 #[test]
685 fn parse_body_field_nested() {
686 let expr = parse("${body.user.city}").unwrap();
687 assert_eq!(
688 expr,
689 Expr::BodyField(vec![
690 PathSegment::Key("user".to_string()),
691 PathSegment::Key("city".to_string()),
692 ])
693 );
694 }
695
696 #[test]
697 fn parse_body_field_array_index() {
698 let expr = parse("${body.items.0}").unwrap();
699 assert_eq!(
700 expr,
701 Expr::BodyField(vec![
702 PathSegment::Key("items".to_string()),
703 PathSegment::Index(0),
704 ])
705 );
706 }
707
708 #[test]
709 fn parse_body_field_array_nested() {
710 let expr = parse("${body.users.0.name}").unwrap();
711 assert_eq!(
712 expr,
713 Expr::BodyField(vec![
714 PathSegment::Key("users".to_string()),
715 PathSegment::Index(0),
716 PathSegment::Key("name".to_string()),
717 ])
718 );
719 }
720
721 #[test]
722 fn parse_body_field_empty_segment_error() {
723 let result = parse("${body.}");
724 assert!(result.is_err());
725 }
726
727 #[test]
728 fn parse_body_field_exact_still_works() {
729 let expr = parse("${body}").unwrap();
731 assert_eq!(expr, Expr::Body);
732 }
733
734 #[test]
735 fn parse_body_field_double_dots_error() {
736 let result = parse("${body..name}");
738 assert!(result.is_err());
739 }
740
741 #[test]
742 fn parse_body_field_index_only() {
743 let expr = parse("${body.0}").unwrap();
745 assert_eq!(expr, Expr::BodyField(vec![PathSegment::Index(0)]));
746 }
747
748 #[test]
749 fn parse_body_field_leading_zero_is_key() {
750 let expr = parse("${body.01}").unwrap();
752 assert_eq!(
753 expr,
754 Expr::BodyField(vec![PathSegment::Key("01".to_string())])
755 );
756 }
757}
758
759#[cfg(test)]
760mod body_field_eval_tests {
761 use crate::SimpleLanguage;
762 use camel_language_api::Language;
763 use camel_language_api::{Body, Exchange, Value};
764 use serde_json::json;
765
766 fn eval(expr_str: &str, body: Body) -> Value {
767 let mut ex = Exchange::default();
768 ex.input.body = body;
769 let lang = SimpleLanguage;
770 lang.create_expression(expr_str)
771 .unwrap()
772 .evaluate(&ex)
773 .unwrap()
774 }
775
776 #[test]
777 fn body_field_simple_key() {
778 let result = eval("${body.name}", Body::Json(json!({"name": "Alice"})));
779 assert_eq!(result, json!("Alice"));
780 }
781
782 #[test]
783 fn body_field_number_value() {
784 let result = eval("${body.age}", Body::Json(json!({"age": 30})));
785 assert_eq!(result, json!(30));
786 }
787
788 #[test]
789 fn body_field_bool_value() {
790 let result = eval("${body.active}", Body::Json(json!({"active": true})));
791 assert_eq!(result, json!(true));
792 }
793
794 #[test]
795 fn body_field_nested() {
796 let result = eval(
797 "${body.user.city}",
798 Body::Json(json!({"user": {"city": "Madrid"}})),
799 );
800 assert_eq!(result, json!("Madrid"));
801 }
802
803 #[test]
804 fn body_field_array_index() {
805 let result = eval("${body.items.0}", Body::Json(json!({"items": ["a", "b"]})));
806 assert_eq!(result, json!("a"));
807 }
808
809 #[test]
810 fn body_field_array_nested() {
811 let result = eval(
812 "${body.users.0.name}",
813 Body::Json(json!({"users": [{"name": "Bob"}]})),
814 );
815 assert_eq!(result, json!("Bob"));
816 }
817
818 #[test]
819 fn body_field_missing_key_returns_null() {
820 let result = eval("${body.missing}", Body::Json(json!({"name": "Alice"})));
821 assert_eq!(result, Value::Null);
822 }
823
824 #[test]
825 fn body_field_missing_nested_returns_null() {
826 let result = eval("${body.a.b.c}", Body::Json(json!({"a": {"x": 1}})));
827 assert_eq!(result, Value::Null);
828 }
829
830 #[test]
831 fn body_field_out_of_bounds_index_returns_null() {
832 let result = eval("${body.items.5}", Body::Json(json!({"items": ["a"]})));
833 assert_eq!(result, Value::Null);
834 }
835
836 #[test]
837 fn body_field_non_json_body_returns_null() {
838 let result = eval(
839 "${body.name}",
840 Body::Text(r#"{"name":"Alice"}"#.to_string()),
841 );
842 assert_eq!(result, Value::Null);
843 }
844
845 #[test]
846 fn body_field_empty_body_returns_null() {
847 let result = eval("${body.name}", Body::Empty);
848 assert_eq!(result, Value::Null);
849 }
850
851 #[test]
852 fn body_field_in_interpolation() {
853 let result = eval("Hello ${body.name}!", Body::Json(json!({"name": "Alice"})));
854 assert_eq!(result, json!("Hello Alice!"));
855 }
856
857 #[test]
858 fn body_field_in_predicate_true() {
859 let lang = SimpleLanguage;
860 let mut ex = Exchange::default();
861 ex.input.body = Body::Json(json!({"status": "active"}));
862 let result = lang
863 .create_expression("${body.status} == 'active'")
864 .unwrap()
865 .evaluate(&ex)
866 .unwrap();
867 assert_eq!(result, Value::Bool(true));
868 }
869
870 #[test]
871 fn body_field_in_predicate_false() {
872 let lang = SimpleLanguage;
873 let mut ex = Exchange::default();
874 ex.input.body = Body::Json(json!({"status": "inactive"}));
875 let result = lang
876 .create_expression("${body.status} == 'active'")
877 .unwrap()
878 .evaluate(&ex)
879 .unwrap();
880 assert_eq!(result, Value::Bool(false));
881 }
882
883 #[test]
884 fn body_field_bytes_body_returns_null() {
885 let result = eval(
888 "${body.name}",
889 Body::Text(r#"{"name":"Alice"}"#.to_string()),
890 );
891 assert_eq!(result, Value::Null);
892 }
893
894 #[test]
895 fn body_field_json_null_value_returns_null() {
896 let result = eval(
898 "${body.name}",
899 Body::Json(serde_json::json!({"name": null})),
900 );
901 assert_eq!(result, Value::Null);
902 }
903
904 #[test]
905 fn body_field_numeric_predicate() {
906 let lang = SimpleLanguage;
909 let mut ex = Exchange::default();
910 ex.input.body = Body::Json(json!({"score": 42.0}));
911 let result = lang
912 .create_expression("${body.score} == 42")
913 .unwrap()
914 .evaluate(&ex)
915 .unwrap();
916 assert_eq!(result, Value::Bool(true));
917 }
918
919 #[test]
920 fn body_bytes_utf8_returns_string() {
921 let lang = SimpleLanguage;
923 let mut ex = Exchange::default();
924 ex.input.body = Body::from(b"hello from bytes".to_vec());
925 let val = lang
926 .create_expression("${body}")
927 .unwrap()
928 .evaluate(&ex)
929 .unwrap();
930 assert_eq!(val, Value::String("hello from bytes".to_string()));
931 }
932
933 #[test]
934 fn body_json_returns_serialized_string() {
935 let lang = SimpleLanguage;
937 let mut ex = Exchange::default();
938 ex.input.body = Body::Json(json!({"msg": "world"}));
939 let val = lang
940 .create_expression("${body}")
941 .unwrap()
942 .evaluate(&ex)
943 .unwrap();
944 let s = match val {
946 Value::String(s) => s,
947 other => panic!("expected String, got {other:?}"),
948 };
949 assert!(!s.is_empty(), "${{body}} on Body::Json should not be empty");
950 let parsed: serde_json::Value = serde_json::from_str(&s).unwrap();
951 assert_eq!(parsed["msg"], "world");
952 }
953
954 #[test]
955 fn body_bytes_in_interpolation() {
956 let lang = SimpleLanguage;
958 let mut ex = Exchange::default();
959 ex.input.body = Body::from(b"ping".to_vec());
960 let val = lang
961 .create_expression("Received: ${body}")
962 .unwrap()
963 .evaluate(&ex)
964 .unwrap();
965 assert_eq!(val, Value::String("Received: ping".to_string()));
966 }
967}