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