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::String("".to_string()));
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_null_expression() {
214 let lang = SimpleLanguage;
215 let expr = lang.create_expression("null").unwrap();
216 let ex = exchange_with_body("test");
217 let val = expr.evaluate(&ex).unwrap();
218 assert_eq!(val, Value::Null);
219 }
220
221 #[test]
222 fn test_predicate_null_is_false() {
223 let lang = SimpleLanguage;
224 let pred = lang.create_predicate("${header.missing}").unwrap();
225 let ex = exchange_with_body("test");
226 assert!(!pred.matches(&ex).unwrap());
227 }
228
229 #[test]
230 fn test_predicate_non_null_is_true() {
231 let lang = SimpleLanguage;
232 let pred = lang.create_predicate("${header.type}").unwrap();
233 let ex = exchange_with_header("type", "order");
234 assert!(pred.matches(&ex).unwrap());
235 }
236
237 #[test]
238 fn test_contains_not_found() {
239 let lang = SimpleLanguage;
240 let pred = lang.create_predicate("${body} contains 'xyz'").unwrap();
241 let ex = exchange_with_body("hello world");
242 assert!(!pred.matches(&ex).unwrap());
243 }
244
245 #[test]
246 fn test_less_than_or_equal() {
247 let lang = SimpleLanguage;
248 let pred = lang.create_predicate("${header.age} <= 18").unwrap();
249 let mut ex = Exchange::new(Message::default());
250 ex.input.set_header("age", Value::Number(18.into()));
251 assert!(pred.matches(&ex).unwrap());
252 }
253
254 #[test]
257 fn test_interpolated_text_with_header() {
258 let lang = SimpleLanguage;
260 let expr = lang
261 .create_expression("Exchange #${header.CamelTimerCounter}")
262 .unwrap();
263 let ex = exchange_with_header("CamelTimerCounter", "42");
264 let val = expr.evaluate(&ex).unwrap();
265 assert_eq!(val, Value::String("Exchange #42".to_string()));
266 }
267
268 #[test]
269 fn test_interpolated_text_with_body() {
270 let lang = SimpleLanguage;
272 let expr = lang.create_expression("Got ${body}").unwrap();
273 let ex = exchange_with_body("hello");
274 let val = expr.evaluate(&ex).unwrap();
275 assert_eq!(val, Value::String("Got hello".to_string()));
276 }
277
278 #[test]
279 fn test_interpolated_multiple_expressions() {
280 let lang = SimpleLanguage;
282 let expr = lang
283 .create_expression("Transformed: ${body} (source=${header.source})")
284 .unwrap();
285 let mut msg = Message::new("data");
286 msg.set_header("source", Value::String("kafka".to_string()));
287 let ex = Exchange::new(msg);
288 let val = expr.evaluate(&ex).unwrap();
289 assert_eq!(
290 val,
291 Value::String("Transformed: data (source=kafka)".to_string())
292 );
293 }
294
295 #[test]
296 fn test_interpolated_missing_header_becomes_empty() {
297 let lang = SimpleLanguage;
299 let expr = lang
300 .create_expression("prefix-${header.missing}-suffix")
301 .unwrap();
302 let ex = exchange_with_body("x");
303 let val = expr.evaluate(&ex).unwrap();
304 assert_eq!(val, Value::String("prefix--suffix".to_string()));
305 }
306
307 #[test]
308 fn test_interpolated_text_only_no_expressions() {
309 let lang = SimpleLanguage;
312 let expr = lang.create_expression("Hello World").unwrap();
313 let ex = exchange_with_body("x");
314 let val = expr.evaluate(&ex).unwrap();
315 assert_eq!(val, Value::String("Hello World".to_string()));
316 }
317
318 #[test]
319 fn test_interpolated_unclosed_brace_treated_as_literal() {
320 let lang = SimpleLanguage;
323 let expr = lang.create_expression("Got ${body").unwrap();
324 let ex = exchange_with_body("hello");
325 let val = expr.evaluate(&ex).unwrap();
326 assert_eq!(val, Value::String("Got ${body".to_string()));
327 }
328
329 #[test]
330 fn test_operator_inside_string_literal_not_split() {
331 let lang = SimpleLanguage;
334 let result = lang.create_expression("'a>=b'");
335 let val = result
336 .unwrap()
337 .evaluate(&Exchange::new(Message::default()))
338 .unwrap();
339 assert_eq!(
340 val,
341 Value::String("a>=b".to_string()),
342 "string literal 'a>=b' should be parsed as a plain string, not split on >="
343 );
344 }
345
346 #[test]
347 fn test_header_eq_string_literal_containing_operator() {
348 let lang = SimpleLanguage;
351 let pred = lang.create_predicate("${header.x} == 'a>=b'").unwrap();
352 let ex = exchange_with_header("x", "a>=b");
353 assert!(
354 pred.matches(&ex).unwrap(),
355 "predicate should match when header equals 'a>=b'"
356 );
357 }
358}
359
360#[cfg(test)]
361mod body_field_parser_tests {
362 use crate::parser::{Expr, PathSegment, parse};
363
364 #[test]
365 fn parse_body_field_simple_key() {
366 let expr = parse("${body.name}").unwrap();
367 assert_eq!(
368 expr,
369 Expr::BodyField(vec![PathSegment::Key("name".to_string())])
370 );
371 }
372
373 #[test]
374 fn parse_body_field_nested() {
375 let expr = parse("${body.user.city}").unwrap();
376 assert_eq!(
377 expr,
378 Expr::BodyField(vec![
379 PathSegment::Key("user".to_string()),
380 PathSegment::Key("city".to_string()),
381 ])
382 );
383 }
384
385 #[test]
386 fn parse_body_field_array_index() {
387 let expr = parse("${body.items.0}").unwrap();
388 assert_eq!(
389 expr,
390 Expr::BodyField(vec![
391 PathSegment::Key("items".to_string()),
392 PathSegment::Index(0),
393 ])
394 );
395 }
396
397 #[test]
398 fn parse_body_field_array_nested() {
399 let expr = parse("${body.users.0.name}").unwrap();
400 assert_eq!(
401 expr,
402 Expr::BodyField(vec![
403 PathSegment::Key("users".to_string()),
404 PathSegment::Index(0),
405 PathSegment::Key("name".to_string()),
406 ])
407 );
408 }
409
410 #[test]
411 fn parse_body_field_empty_segment_error() {
412 let result = parse("${body.}");
413 assert!(result.is_err());
414 }
415
416 #[test]
417 fn parse_body_field_exact_still_works() {
418 let expr = parse("${body}").unwrap();
420 assert_eq!(expr, Expr::Body);
421 }
422
423 #[test]
424 fn parse_body_field_double_dots_error() {
425 let result = parse("${body..name}");
427 assert!(result.is_err());
428 }
429
430 #[test]
431 fn parse_body_field_index_only() {
432 let expr = parse("${body.0}").unwrap();
434 assert_eq!(expr, Expr::BodyField(vec![PathSegment::Index(0)]));
435 }
436
437 #[test]
438 fn parse_body_field_leading_zero_is_key() {
439 let expr = parse("${body.01}").unwrap();
441 assert_eq!(
442 expr,
443 Expr::BodyField(vec![PathSegment::Key("01".to_string())])
444 );
445 }
446}
447
448#[cfg(test)]
449mod body_field_eval_tests {
450 use crate::SimpleLanguage;
451 use camel_language_api::Language;
452 use camel_language_api::{Body, Exchange, Value};
453 use serde_json::json;
454
455 fn eval(expr_str: &str, body: Body) -> Value {
456 let mut ex = Exchange::default();
457 ex.input.body = body;
458 let lang = SimpleLanguage;
459 lang.create_expression(expr_str)
460 .unwrap()
461 .evaluate(&ex)
462 .unwrap()
463 }
464
465 #[test]
466 fn body_field_simple_key() {
467 let result = eval("${body.name}", Body::Json(json!({"name": "Alice"})));
468 assert_eq!(result, json!("Alice"));
469 }
470
471 #[test]
472 fn body_field_number_value() {
473 let result = eval("${body.age}", Body::Json(json!({"age": 30})));
474 assert_eq!(result, json!(30));
475 }
476
477 #[test]
478 fn body_field_bool_value() {
479 let result = eval("${body.active}", Body::Json(json!({"active": true})));
480 assert_eq!(result, json!(true));
481 }
482
483 #[test]
484 fn body_field_nested() {
485 let result = eval(
486 "${body.user.city}",
487 Body::Json(json!({"user": {"city": "Madrid"}})),
488 );
489 assert_eq!(result, json!("Madrid"));
490 }
491
492 #[test]
493 fn body_field_array_index() {
494 let result = eval("${body.items.0}", Body::Json(json!({"items": ["a", "b"]})));
495 assert_eq!(result, json!("a"));
496 }
497
498 #[test]
499 fn body_field_array_nested() {
500 let result = eval(
501 "${body.users.0.name}",
502 Body::Json(json!({"users": [{"name": "Bob"}]})),
503 );
504 assert_eq!(result, json!("Bob"));
505 }
506
507 #[test]
508 fn body_field_missing_key_returns_null() {
509 let result = eval("${body.missing}", Body::Json(json!({"name": "Alice"})));
510 assert_eq!(result, Value::Null);
511 }
512
513 #[test]
514 fn body_field_missing_nested_returns_null() {
515 let result = eval("${body.a.b.c}", Body::Json(json!({"a": {"x": 1}})));
516 assert_eq!(result, Value::Null);
517 }
518
519 #[test]
520 fn body_field_out_of_bounds_index_returns_null() {
521 let result = eval("${body.items.5}", Body::Json(json!({"items": ["a"]})));
522 assert_eq!(result, Value::Null);
523 }
524
525 #[test]
526 fn body_field_non_json_body_returns_null() {
527 let result = eval(
528 "${body.name}",
529 Body::Text(r#"{"name":"Alice"}"#.to_string()),
530 );
531 assert_eq!(result, Value::Null);
532 }
533
534 #[test]
535 fn body_field_empty_body_returns_null() {
536 let result = eval("${body.name}", Body::Empty);
537 assert_eq!(result, Value::Null);
538 }
539
540 #[test]
541 fn body_field_in_interpolation() {
542 let result = eval("Hello ${body.name}!", Body::Json(json!({"name": "Alice"})));
543 assert_eq!(result, json!("Hello Alice!"));
544 }
545
546 #[test]
547 fn body_field_in_predicate_true() {
548 let lang = SimpleLanguage;
549 let mut ex = Exchange::default();
550 ex.input.body = Body::Json(json!({"status": "active"}));
551 let result = lang
552 .create_expression("${body.status} == 'active'")
553 .unwrap()
554 .evaluate(&ex)
555 .unwrap();
556 assert_eq!(result, Value::Bool(true));
557 }
558
559 #[test]
560 fn body_field_in_predicate_false() {
561 let lang = SimpleLanguage;
562 let mut ex = Exchange::default();
563 ex.input.body = Body::Json(json!({"status": "inactive"}));
564 let result = lang
565 .create_expression("${body.status} == 'active'")
566 .unwrap()
567 .evaluate(&ex)
568 .unwrap();
569 assert_eq!(result, Value::Bool(false));
570 }
571
572 #[test]
573 fn body_field_bytes_body_returns_null() {
574 let result = eval(
577 "${body.name}",
578 Body::Text(r#"{"name":"Alice"}"#.to_string()),
579 );
580 assert_eq!(result, Value::Null);
581 }
582
583 #[test]
584 fn body_field_json_null_value_returns_null() {
585 let result = eval(
587 "${body.name}",
588 Body::Json(serde_json::json!({"name": null})),
589 );
590 assert_eq!(result, Value::Null);
591 }
592
593 #[test]
594 fn body_field_numeric_predicate() {
595 let lang = SimpleLanguage;
598 let mut ex = Exchange::default();
599 ex.input.body = Body::Json(json!({"score": 42.0}));
600 let result = lang
601 .create_expression("${body.score} == 42")
602 .unwrap()
603 .evaluate(&ex)
604 .unwrap();
605 assert_eq!(result, Value::Bool(true));
606 }
607
608 #[test]
609 fn body_bytes_utf8_returns_string() {
610 let lang = SimpleLanguage;
612 let mut ex = Exchange::default();
613 ex.input.body = Body::from(b"hello from bytes".to_vec());
614 let val = lang
615 .create_expression("${body}")
616 .unwrap()
617 .evaluate(&ex)
618 .unwrap();
619 assert_eq!(val, Value::String("hello from bytes".to_string()));
620 }
621
622 #[test]
623 fn body_json_returns_serialized_string() {
624 let lang = SimpleLanguage;
626 let mut ex = Exchange::default();
627 ex.input.body = Body::Json(json!({"msg": "world"}));
628 let val = lang
629 .create_expression("${body}")
630 .unwrap()
631 .evaluate(&ex)
632 .unwrap();
633 let s = match val {
635 Value::String(s) => s,
636 other => panic!("expected String, got {other:?}"),
637 };
638 assert!(!s.is_empty(), "${{body}} on Body::Json should not be empty");
639 let parsed: serde_json::Value = serde_json::from_str(&s).unwrap();
640 assert_eq!(parsed["msg"], "world");
641 }
642
643 #[test]
644 fn body_bytes_in_interpolation() {
645 let lang = SimpleLanguage;
647 let mut ex = Exchange::default();
648 ex.input.body = Body::from(b"ping".to_vec());
649 let val = lang
650 .create_expression("Received: ${body}")
651 .unwrap()
652 .evaluate(&ex)
653 .unwrap();
654 assert_eq!(val, Value::String("Received: ping".to_string()));
655 }
656}