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