camel_language_simple/
lib.rs1mod evaluator;
2mod parser;
3
4use camel_api::exchange::Exchange;
5use camel_language_api::{Expression, Language, LanguageError, Predicate};
6
7pub struct SimpleLanguage;
8
9struct SimpleExpression(parser::Expr);
10struct SimplePredicate(parser::Expr);
11
12impl Expression for SimpleExpression {
13 fn evaluate(&self, exchange: &Exchange) -> Result<camel_api::Value, LanguageError> {
14 evaluator::evaluate(&self.0, exchange)
15 }
16}
17
18impl Predicate for SimplePredicate {
19 fn matches(&self, exchange: &Exchange) -> Result<bool, LanguageError> {
20 let val = evaluator::evaluate(&self.0, exchange)?;
21 Ok(match &val {
22 camel_api::Value::Bool(b) => *b,
23 camel_api::Value::Null => false,
24 _ => true,
25 })
26 }
27}
28
29impl Language for SimpleLanguage {
30 fn name(&self) -> &'static str {
31 "simple"
32 }
33
34 fn create_expression(&self, script: &str) -> Result<Box<dyn Expression>, LanguageError> {
35 let ast = parser::parse(script)?;
36 Ok(Box::new(SimpleExpression(ast)))
37 }
38
39 fn create_predicate(&self, script: &str) -> Result<Box<dyn Predicate>, LanguageError> {
40 let ast = parser::parse(script)?;
41 Ok(Box::new(SimplePredicate(ast)))
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use super::SimpleLanguage;
48 use camel_api::{Value, exchange::Exchange, message::Message};
49 use camel_language_api::Language;
50
51 fn exchange_with_header(key: &str, val: &str) -> Exchange {
52 let mut msg = Message::default();
53 msg.set_header(key, Value::String(val.to_string()));
54 Exchange::new(msg)
55 }
56
57 fn exchange_with_body(body: &str) -> Exchange {
58 Exchange::new(Message::new(body))
59 }
60
61 #[test]
62 fn test_header_equals_string() {
63 let lang = SimpleLanguage;
64 let pred = lang.create_predicate("${header.type} == 'order'").unwrap();
65 let ex = exchange_with_header("type", "order");
66 assert!(pred.matches(&ex).unwrap());
67 }
68
69 #[test]
70 fn test_header_not_equals() {
71 let lang = SimpleLanguage;
72 let pred = lang.create_predicate("${header.type} != 'order'").unwrap();
73 let ex = exchange_with_header("type", "invoice");
74 assert!(pred.matches(&ex).unwrap());
75 }
76
77 #[test]
78 fn test_body_contains() {
79 let lang = SimpleLanguage;
80 let pred = lang.create_predicate("${body} contains 'hello'").unwrap();
81 let ex = exchange_with_body("say hello world");
82 assert!(pred.matches(&ex).unwrap());
83 }
84
85 #[test]
86 fn test_header_null_check() {
87 let lang = SimpleLanguage;
88 let pred = lang.create_predicate("${header.missing} == null").unwrap();
89 let ex = exchange_with_body("anything");
90 assert!(pred.matches(&ex).unwrap());
91 }
92
93 #[test]
94 fn test_header_not_null() {
95 let lang = SimpleLanguage;
96 let pred = lang.create_predicate("${header.type} != null").unwrap();
97 let ex = exchange_with_header("type", "order");
98 assert!(pred.matches(&ex).unwrap());
99 }
100
101 #[test]
102 fn test_expression_header_value() {
103 let lang = SimpleLanguage;
104 let expr = lang.create_expression("${header.type}").unwrap();
105 let ex = exchange_with_header("type", "order");
106 let val = expr.evaluate(&ex).unwrap();
107 assert_eq!(val, Value::String("order".to_string()));
108 }
109
110 #[test]
111 fn test_expression_body() {
112 let lang = SimpleLanguage;
113 let expr = lang.create_expression("${body}").unwrap();
114 let ex = exchange_with_body("hello");
115 let val = expr.evaluate(&ex).unwrap();
116 assert_eq!(val, Value::String("hello".to_string()));
117 }
118
119 #[test]
120 fn test_numeric_comparison() {
121 let lang = SimpleLanguage;
122 let pred = lang.create_predicate("${header.age} > 18").unwrap();
123 let mut ex = Exchange::new(Message::default());
124 ex.input.set_header("age", Value::Number(25.into()));
125 assert!(pred.matches(&ex).unwrap());
126 }
127
128 #[test]
131 fn test_empty_body() {
132 let lang = SimpleLanguage;
133 let expr = lang.create_expression("${body}").unwrap();
134 let ex = Exchange::new(Message::default());
135 let val = expr.evaluate(&ex).unwrap();
136 assert_eq!(val, Value::String("".to_string()));
137 }
138
139 #[test]
140 fn test_parse_error_unrecognized_token() {
141 let lang = SimpleLanguage;
142 let result = lang.create_expression("@@invalid@@");
143 assert!(result.is_err());
144 }
145
146 #[test]
147 fn test_empty_header_key_is_parse_error() {
148 let lang = SimpleLanguage;
149 let result = lang.create_expression("${header.}");
150 let err = result.err().expect("should be a parse error");
151 let err = format!("{err}");
152 assert!(
153 err.contains("empty"),
154 "error should mention empty key, got: {err}"
155 );
156 }
157
158 #[test]
159 fn test_empty_exchange_property_key_is_parse_error() {
160 let lang = SimpleLanguage;
161 let result = lang.create_expression("${exchangeProperty.}");
162 let err = result.err().expect("should be a parse error");
163 let err = format!("{err}");
164 assert!(
165 err.contains("empty"),
166 "error should mention empty key, got: {err}"
167 );
168 }
169
170 #[test]
171 fn test_missing_header_returns_null() {
172 let lang = SimpleLanguage;
173 let expr = lang.create_expression("${header.nonexistent}").unwrap();
174 let ex = exchange_with_body("anything");
175 let val = expr.evaluate(&ex).unwrap();
176 assert_eq!(val, Value::Null);
177 }
178
179 #[test]
180 fn test_exchange_property_expression() {
181 let lang = SimpleLanguage;
182 let expr = lang
183 .create_expression("${exchangeProperty.myProp}")
184 .unwrap();
185 let mut ex = exchange_with_body("test");
186 ex.set_property("myProp".to_string(), Value::String("propVal".to_string()));
187 let val = expr.evaluate(&ex).unwrap();
188 assert_eq!(val, Value::String("propVal".to_string()));
189 }
190
191 #[test]
192 fn test_missing_property_returns_null() {
193 let lang = SimpleLanguage;
194 let expr = lang
195 .create_expression("${exchangeProperty.missing}")
196 .unwrap();
197 let ex = exchange_with_body("test");
198 let val = expr.evaluate(&ex).unwrap();
199 assert_eq!(val, Value::Null);
200 }
201
202 #[test]
203 fn test_string_literal_expression() {
204 let lang = SimpleLanguage;
205 let expr = lang.create_expression("'hello'").unwrap();
206 let ex = exchange_with_body("test");
207 let val = expr.evaluate(&ex).unwrap();
208 assert_eq!(val, Value::String("hello".to_string()));
209 }
210
211 #[test]
212 fn test_null_expression() {
213 let lang = SimpleLanguage;
214 let expr = lang.create_expression("null").unwrap();
215 let ex = exchange_with_body("test");
216 let val = expr.evaluate(&ex).unwrap();
217 assert_eq!(val, Value::Null);
218 }
219
220 #[test]
221 fn test_predicate_null_is_false() {
222 let lang = SimpleLanguage;
223 let pred = lang.create_predicate("${header.missing}").unwrap();
224 let ex = exchange_with_body("test");
225 assert!(!pred.matches(&ex).unwrap());
226 }
227
228 #[test]
229 fn test_predicate_non_null_is_true() {
230 let lang = SimpleLanguage;
231 let pred = lang.create_predicate("${header.type}").unwrap();
232 let ex = exchange_with_header("type", "order");
233 assert!(pred.matches(&ex).unwrap());
234 }
235
236 #[test]
237 fn test_contains_not_found() {
238 let lang = SimpleLanguage;
239 let pred = lang.create_predicate("${body} contains 'xyz'").unwrap();
240 let ex = exchange_with_body("hello world");
241 assert!(!pred.matches(&ex).unwrap());
242 }
243
244 #[test]
245 fn test_less_than_or_equal() {
246 let lang = SimpleLanguage;
247 let pred = lang.create_predicate("${header.age} <= 18").unwrap();
248 let mut ex = Exchange::new(Message::default());
249 ex.input.set_header("age", Value::Number(18.into()));
250 assert!(pred.matches(&ex).unwrap());
251 }
252
253 #[test]
254 fn test_operator_inside_string_literal_not_split() {
255 let lang = SimpleLanguage;
258 let result = lang.create_expression("'a>=b'");
259 let val = result
260 .unwrap()
261 .evaluate(&Exchange::new(Message::default()))
262 .unwrap();
263 assert_eq!(
264 val,
265 Value::String("a>=b".to_string()),
266 "string literal 'a>=b' should be parsed as a plain string, not split on >="
267 );
268 }
269
270 #[test]
271 fn test_header_eq_string_literal_containing_operator() {
272 let lang = SimpleLanguage;
275 let pred = lang.create_predicate("${header.x} == 'a>=b'").unwrap();
276 let ex = exchange_with_header("x", "a>=b");
277 assert!(
278 pred.matches(&ex).unwrap(),
279 "predicate should match when header equals 'a>=b'"
280 );
281 }
282}