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
622 .create_predicate("${header.missing} contains 'x'")
623 .unwrap();
624 let ex = exchange_with_body("test");
625 assert!(!pred.matches(&ex).unwrap());
626 }
627
628 #[test]
629 fn test_contains_null_right_is_false() {
630 let lang = SimpleLanguage;
631 let pred = lang.create_predicate("${body} contains null").unwrap();
632 let ex = exchange_with_body("test");
633 assert!(!pred.matches(&ex).unwrap());
634 }
635
636 #[test]
637 fn test_contains_without_spaces() {
638 let lang = SimpleLanguage;
639 let pred = lang.create_predicate("${body}contains'hello'").unwrap();
640 let ex = exchange_with_body("say hello world");
641 assert!(pred.matches(&ex).unwrap());
642 }
643
644 #[test]
645 fn test_non_finite_number_parse_error() {
646 let lang = SimpleLanguage;
647 let result = lang.create_expression(
649 "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999",
650 );
651 assert!(result.is_err(), "non-finite number should be a parse error");
652 }
653
654 #[test]
655 fn test_interpolation_with_empty_body_still_produces_text() {
656 let lang = SimpleLanguage;
657 let expr = lang.create_expression("Got ${body}").unwrap();
658 let ex = Exchange::new(Message::default());
659 let val = expr.evaluate(&ex).unwrap();
660 assert_eq!(val, Value::String("Got ".to_string()));
661 }
662
663 #[test]
664 fn test_interpolation_with_empty_body_and_trailing_text() {
665 let lang = SimpleLanguage;
666 let expr = lang.create_expression("${body} tail").unwrap();
667 let ex = Exchange::new(Message::default());
668 let val = expr.evaluate(&ex).unwrap();
669 assert_eq!(val, Value::String(" tail".to_string()));
670 }
671}
672
673#[cfg(test)]
674mod body_field_parser_tests {
675 use crate::parser::{Expr, PathSegment, parse};
676
677 #[test]
678 fn parse_body_field_simple_key() {
679 let expr = parse("${body.name}").unwrap();
680 assert_eq!(
681 expr,
682 Expr::BodyField(vec![PathSegment::Key("name".to_string())])
683 );
684 }
685
686 #[test]
687 fn parse_body_field_nested() {
688 let expr = parse("${body.user.city}").unwrap();
689 assert_eq!(
690 expr,
691 Expr::BodyField(vec![
692 PathSegment::Key("user".to_string()),
693 PathSegment::Key("city".to_string()),
694 ])
695 );
696 }
697
698 #[test]
699 fn parse_body_field_array_index() {
700 let expr = parse("${body.items.0}").unwrap();
701 assert_eq!(
702 expr,
703 Expr::BodyField(vec![
704 PathSegment::Key("items".to_string()),
705 PathSegment::Index(0),
706 ])
707 );
708 }
709
710 #[test]
711 fn parse_body_field_array_nested() {
712 let expr = parse("${body.users.0.name}").unwrap();
713 assert_eq!(
714 expr,
715 Expr::BodyField(vec![
716 PathSegment::Key("users".to_string()),
717 PathSegment::Index(0),
718 PathSegment::Key("name".to_string()),
719 ])
720 );
721 }
722
723 #[test]
724 fn parse_body_field_empty_segment_error() {
725 let result = parse("${body.}");
726 assert!(result.is_err());
727 }
728
729 #[test]
730 fn parse_body_field_exact_still_works() {
731 let expr = parse("${body}").unwrap();
733 assert_eq!(expr, Expr::Body);
734 }
735
736 #[test]
737 fn parse_body_field_double_dots_error() {
738 let result = parse("${body..name}");
740 assert!(result.is_err());
741 }
742
743 #[test]
744 fn parse_body_field_index_only() {
745 let expr = parse("${body.0}").unwrap();
747 assert_eq!(expr, Expr::BodyField(vec![PathSegment::Index(0)]));
748 }
749
750 #[test]
751 fn parse_body_field_leading_zero_is_key() {
752 let expr = parse("${body.01}").unwrap();
754 assert_eq!(
755 expr,
756 Expr::BodyField(vec![PathSegment::Key("01".to_string())])
757 );
758 }
759}
760
761#[cfg(test)]
762mod body_field_eval_tests {
763 use crate::SimpleLanguage;
764 use camel_language_api::Language;
765 use camel_language_api::{Body, Exchange, Value};
766 use serde_json::json;
767
768 fn eval(expr_str: &str, body: Body) -> Value {
769 let mut ex = Exchange::default();
770 ex.input.body = body;
771 let lang = SimpleLanguage;
772 lang.create_expression(expr_str)
773 .unwrap()
774 .evaluate(&ex)
775 .unwrap()
776 }
777
778 #[test]
779 fn body_field_simple_key() {
780 let result = eval("${body.name}", Body::Json(json!({"name": "Alice"})));
781 assert_eq!(result, json!("Alice"));
782 }
783
784 #[test]
785 fn body_field_number_value() {
786 let result = eval("${body.age}", Body::Json(json!({"age": 30})));
787 assert_eq!(result, json!(30));
788 }
789
790 #[test]
791 fn body_field_bool_value() {
792 let result = eval("${body.active}", Body::Json(json!({"active": true})));
793 assert_eq!(result, json!(true));
794 }
795
796 #[test]
797 fn body_field_nested() {
798 let result = eval(
799 "${body.user.city}",
800 Body::Json(json!({"user": {"city": "Madrid"}})),
801 );
802 assert_eq!(result, json!("Madrid"));
803 }
804
805 #[test]
806 fn body_field_array_index() {
807 let result = eval("${body.items.0}", Body::Json(json!({"items": ["a", "b"]})));
808 assert_eq!(result, json!("a"));
809 }
810
811 #[test]
812 fn body_field_array_nested() {
813 let result = eval(
814 "${body.users.0.name}",
815 Body::Json(json!({"users": [{"name": "Bob"}]})),
816 );
817 assert_eq!(result, json!("Bob"));
818 }
819
820 #[test]
821 fn body_field_missing_key_returns_null() {
822 let result = eval("${body.missing}", Body::Json(json!({"name": "Alice"})));
823 assert_eq!(result, Value::Null);
824 }
825
826 #[test]
827 fn body_field_missing_nested_returns_null() {
828 let result = eval("${body.a.b.c}", Body::Json(json!({"a": {"x": 1}})));
829 assert_eq!(result, Value::Null);
830 }
831
832 #[test]
833 fn body_field_out_of_bounds_index_returns_null() {
834 let result = eval("${body.items.5}", Body::Json(json!({"items": ["a"]})));
835 assert_eq!(result, Value::Null);
836 }
837
838 #[test]
839 fn body_field_non_json_body_returns_null() {
840 let result = eval(
841 "${body.name}",
842 Body::Text(r#"{"name":"Alice"}"#.to_string()),
843 );
844 assert_eq!(result, Value::Null);
845 }
846
847 #[test]
848 fn body_field_empty_body_returns_null() {
849 let result = eval("${body.name}", Body::Empty);
850 assert_eq!(result, Value::Null);
851 }
852
853 #[test]
854 fn body_field_in_interpolation() {
855 let result = eval("Hello ${body.name}!", Body::Json(json!({"name": "Alice"})));
856 assert_eq!(result, json!("Hello Alice!"));
857 }
858
859 #[test]
860 fn body_field_in_predicate_true() {
861 let lang = SimpleLanguage;
862 let mut ex = Exchange::default();
863 ex.input.body = Body::Json(json!({"status": "active"}));
864 let result = lang
865 .create_expression("${body.status} == 'active'")
866 .unwrap()
867 .evaluate(&ex)
868 .unwrap();
869 assert_eq!(result, Value::Bool(true));
870 }
871
872 #[test]
873 fn body_field_in_predicate_false() {
874 let lang = SimpleLanguage;
875 let mut ex = Exchange::default();
876 ex.input.body = Body::Json(json!({"status": "inactive"}));
877 let result = lang
878 .create_expression("${body.status} == 'active'")
879 .unwrap()
880 .evaluate(&ex)
881 .unwrap();
882 assert_eq!(result, Value::Bool(false));
883 }
884
885 #[test]
886 fn body_field_bytes_body_returns_null() {
887 let result = eval(
890 "${body.name}",
891 Body::Text(r#"{"name":"Alice"}"#.to_string()),
892 );
893 assert_eq!(result, Value::Null);
894 }
895
896 #[test]
897 fn body_field_json_null_value_returns_null() {
898 let result = eval(
900 "${body.name}",
901 Body::Json(serde_json::json!({"name": null})),
902 );
903 assert_eq!(result, Value::Null);
904 }
905
906 #[test]
907 fn body_field_numeric_predicate() {
908 let lang = SimpleLanguage;
911 let mut ex = Exchange::default();
912 ex.input.body = Body::Json(json!({"score": 42.0}));
913 let result = lang
914 .create_expression("${body.score} == 42")
915 .unwrap()
916 .evaluate(&ex)
917 .unwrap();
918 assert_eq!(result, Value::Bool(true));
919 }
920
921 #[test]
922 fn body_bytes_utf8_returns_string() {
923 let lang = SimpleLanguage;
925 let mut ex = Exchange::default();
926 ex.input.body = Body::from(b"hello from bytes".to_vec());
927 let val = lang
928 .create_expression("${body}")
929 .unwrap()
930 .evaluate(&ex)
931 .unwrap();
932 assert_eq!(val, Value::String("hello from bytes".to_string()));
933 }
934
935 #[test]
936 fn body_json_returns_serialized_string() {
937 let lang = SimpleLanguage;
939 let mut ex = Exchange::default();
940 ex.input.body = Body::Json(json!({"msg": "world"}));
941 let val = lang
942 .create_expression("${body}")
943 .unwrap()
944 .evaluate(&ex)
945 .unwrap();
946 let s = match val {
948 Value::String(s) => s,
949 other => panic!("expected String, got {other:?}"),
950 };
951 assert!(!s.is_empty(), "${{body}} on Body::Json should not be empty");
952 let parsed: serde_json::Value = serde_json::from_str(&s).unwrap();
953 assert_eq!(parsed["msg"], "world");
954 }
955
956 #[test]
957 fn body_bytes_in_interpolation() {
958 let lang = SimpleLanguage;
960 let mut ex = Exchange::default();
961 ex.input.body = Body::from(b"ping".to_vec());
962 let val = lang
963 .create_expression("Received: ${body}")
964 .unwrap()
965 .evaluate(&ex)
966 .unwrap();
967 assert_eq!(val, Value::String("Received: ping".to_string()));
968 }
969}