1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
21#[serde(tag = "kind")]
22pub enum Expression {
23 IntLiteral { value: i64 },
26
27 BoolLiteral { value: bool },
29
30 Null,
32
33 Param { index: u32 },
37
38 FieldAccess {
42 object: Box<Expression>,
43 class_name: String,
44 field_name: String,
45 },
46
47 Eq {
50 left: Box<Expression>,
51 right: Box<Expression>,
52 },
53
54 Ne {
56 left: Box<Expression>,
57 right: Box<Expression>,
58 },
59
60 Lt {
62 left: Box<Expression>,
63 right: Box<Expression>,
64 },
65
66 Le {
68 left: Box<Expression>,
69 right: Box<Expression>,
70 },
71
72 Gt {
74 left: Box<Expression>,
75 right: Box<Expression>,
76 },
77
78 Ge {
80 left: Box<Expression>,
81 right: Box<Expression>,
82 },
83
84 And {
87 left: Box<Expression>,
88 right: Box<Expression>,
89 },
90
91 Or {
93 left: Box<Expression>,
94 right: Box<Expression>,
95 },
96
97 Not { operand: Box<Expression> },
99
100 IsNull { operand: Box<Expression> },
102
103 IsNotNull { operand: Box<Expression> },
105
106 Add {
109 left: Box<Expression>,
110 right: Box<Expression>,
111 },
112
113 Sub {
115 left: Box<Expression>,
116 right: Box<Expression>,
117 },
118
119 Mul {
121 left: Box<Expression>,
122 right: Box<Expression>,
123 },
124
125 Div {
127 left: Box<Expression>,
128 right: Box<Expression>,
129 },
130
131 ListContains {
135 list: Box<Expression>,
136 element: Box<Expression>,
137 },
138
139 HostCall {
143 function_name: String,
144 args: Vec<Expression>,
145 },
146
147 IfThenElse {
151 condition: Box<Expression>,
152 then_branch: Box<Expression>,
153 else_branch: Box<Expression>,
154 },
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_int_literal() {
163 let expr = Expression::IntLiteral { value: 42 };
164 assert_eq!(expr, Expression::IntLiteral { value: 42 });
165 }
166
167 #[test]
168 fn test_bool_literal() {
169 let expr = Expression::BoolLiteral { value: true };
170 assert_eq!(expr, Expression::BoolLiteral { value: true });
171 }
172
173 #[test]
174 fn test_null() {
175 let expr = Expression::Null;
176 assert_eq!(expr, Expression::Null);
177 }
178
179 #[test]
180 fn test_param() {
181 let expr = Expression::Param { index: 0 };
182 assert_eq!(expr, Expression::Param { index: 0 });
183 }
184
185 #[test]
186 fn test_field_access() {
187 let expr = Expression::FieldAccess {
188 object: Box::new(Expression::Param { index: 0 }),
189 class_name: "Employee".into(),
190 field_name: "name".into(),
191 };
192
193 match expr {
194 Expression::FieldAccess {
195 object,
196 class_name,
197 field_name,
198 } => {
199 assert_eq!(class_name, "Employee");
200 assert_eq!(field_name, "name");
201 assert_eq!(*object, Expression::Param { index: 0 });
202 }
203 _ => panic!("Expected FieldAccess"),
204 }
205 }
206
207 #[test]
208 fn test_comparison_eq() {
209 let expr = Expression::Eq {
210 left: Box::new(Expression::IntLiteral { value: 1 }),
211 right: Box::new(Expression::IntLiteral { value: 2 }),
212 };
213
214 match expr {
215 Expression::Eq { left, right } => {
216 assert_eq!(*left, Expression::IntLiteral { value: 1 });
217 assert_eq!(*right, Expression::IntLiteral { value: 2 });
218 }
219 _ => panic!("Expected Eq"),
220 }
221 }
222
223 #[test]
224 fn test_serialize_int_literal() {
225 let expr = Expression::IntLiteral { value: 42 };
226 let json = serde_json::to_string(&expr).unwrap();
227 assert!(json.contains("\"kind\":\"IntLiteral\""));
228 assert!(json.contains("\"value\":42"));
229 }
230
231 #[test]
232 fn test_deserialize_int_literal() {
233 let json = r#"{"kind":"IntLiteral","value":42}"#;
234 let expr: Expression = serde_json::from_str(json).unwrap();
235 assert_eq!(expr, Expression::IntLiteral { value: 42 });
236 }
237
238 #[test]
239 fn test_serialize_field_access() {
240 let expr = Expression::FieldAccess {
241 object: Box::new(Expression::Param { index: 0 }),
242 class_name: "Employee".into(),
243 field_name: "name".into(),
244 };
245
246 let json = serde_json::to_string(&expr).unwrap();
247 let deserialized: Expression = serde_json::from_str(&json).unwrap();
248 assert_eq!(expr, deserialized);
249 }
250
251 #[test]
252 fn test_complex_expression() {
253 let expr = Expression::Ne {
255 left: Box::new(Expression::FieldAccess {
256 object: Box::new(Expression::Param { index: 0 }),
257 class_name: "Shift".into(),
258 field_name: "employee".into(),
259 }),
260 right: Box::new(Expression::Null),
261 };
262
263 let json = serde_json::to_string(&expr).unwrap();
265 let deserialized: Expression = serde_json::from_str(&json).unwrap();
266 assert_eq!(expr, deserialized);
267 }
268
269 #[test]
272 fn test_logical_and() {
273 let expr = Expression::And {
274 left: Box::new(Expression::BoolLiteral { value: true }),
275 right: Box::new(Expression::BoolLiteral { value: false }),
276 };
277
278 match expr {
279 Expression::And { left, right } => {
280 assert_eq!(*left, Expression::BoolLiteral { value: true });
281 assert_eq!(*right, Expression::BoolLiteral { value: false });
282 }
283 _ => panic!("Expected And"),
284 }
285 }
286
287 #[test]
288 fn test_logical_or() {
289 let expr = Expression::Or {
290 left: Box::new(Expression::BoolLiteral { value: true }),
291 right: Box::new(Expression::BoolLiteral { value: false }),
292 };
293
294 match expr {
295 Expression::Or { left, right } => {
296 assert_eq!(*left, Expression::BoolLiteral { value: true });
297 assert_eq!(*right, Expression::BoolLiteral { value: false });
298 }
299 _ => panic!("Expected Or"),
300 }
301 }
302
303 #[test]
304 fn test_logical_not() {
305 let expr = Expression::Not {
306 operand: Box::new(Expression::BoolLiteral { value: true }),
307 };
308
309 match expr {
310 Expression::Not { operand } => {
311 assert_eq!(*operand, Expression::BoolLiteral { value: true });
312 }
313 _ => panic!("Expected Not"),
314 }
315 }
316
317 #[test]
318 fn test_is_null() {
319 let expr = Expression::IsNull {
320 operand: Box::new(Expression::Param { index: 0 }),
321 };
322
323 match expr {
324 Expression::IsNull { operand } => {
325 assert_eq!(*operand, Expression::Param { index: 0 });
326 }
327 _ => panic!("Expected IsNull"),
328 }
329 }
330
331 #[test]
332 fn test_is_not_null() {
333 let expr = Expression::IsNotNull {
334 operand: Box::new(Expression::Param { index: 0 }),
335 };
336
337 match expr {
338 Expression::IsNotNull { operand } => {
339 assert_eq!(*operand, Expression::Param { index: 0 });
340 }
341 _ => panic!("Expected IsNotNull"),
342 }
343 }
344
345 #[test]
346 fn test_serialize_logical_and() {
347 let expr = Expression::And {
348 left: Box::new(Expression::BoolLiteral { value: true }),
349 right: Box::new(Expression::BoolLiteral { value: false }),
350 };
351
352 let json = serde_json::to_string(&expr).unwrap();
353 let deserialized: Expression = serde_json::from_str(&json).unwrap();
354 assert_eq!(expr, deserialized);
355 }
356
357 #[test]
360 fn test_arithmetic_add() {
361 let expr = Expression::Add {
362 left: Box::new(Expression::IntLiteral { value: 10 }),
363 right: Box::new(Expression::IntLiteral { value: 20 }),
364 };
365
366 match expr {
367 Expression::Add { left, right } => {
368 assert_eq!(*left, Expression::IntLiteral { value: 10 });
369 assert_eq!(*right, Expression::IntLiteral { value: 20 });
370 }
371 _ => panic!("Expected Add"),
372 }
373 }
374
375 #[test]
376 fn test_arithmetic_sub() {
377 let expr = Expression::Sub {
378 left: Box::new(Expression::IntLiteral { value: 30 }),
379 right: Box::new(Expression::IntLiteral { value: 10 }),
380 };
381
382 match expr {
383 Expression::Sub { left, right } => {
384 assert_eq!(*left, Expression::IntLiteral { value: 30 });
385 assert_eq!(*right, Expression::IntLiteral { value: 10 });
386 }
387 _ => panic!("Expected Sub"),
388 }
389 }
390
391 #[test]
392 fn test_arithmetic_mul() {
393 let expr = Expression::Mul {
394 left: Box::new(Expression::IntLiteral { value: 5 }),
395 right: Box::new(Expression::IntLiteral { value: 3 }),
396 };
397
398 match expr {
399 Expression::Mul { left, right } => {
400 assert_eq!(*left, Expression::IntLiteral { value: 5 });
401 assert_eq!(*right, Expression::IntLiteral { value: 3 });
402 }
403 _ => panic!("Expected Mul"),
404 }
405 }
406
407 #[test]
408 fn test_arithmetic_div() {
409 let expr = Expression::Div {
410 left: Box::new(Expression::IntLiteral { value: 100 }),
411 right: Box::new(Expression::IntLiteral { value: 5 }),
412 };
413
414 match expr {
415 Expression::Div { left, right } => {
416 assert_eq!(*left, Expression::IntLiteral { value: 100 });
417 assert_eq!(*right, Expression::IntLiteral { value: 5 });
418 }
419 _ => panic!("Expected Div"),
420 }
421 }
422
423 #[test]
424 fn test_serialize_arithmetic() {
425 let expr = Expression::Add {
426 left: Box::new(Expression::IntLiteral { value: 10 }),
427 right: Box::new(Expression::IntLiteral { value: 20 }),
428 };
429
430 let json = serde_json::to_string(&expr).unwrap();
431 let deserialized: Expression = serde_json::from_str(&json).unwrap();
432 assert_eq!(expr, deserialized);
433 }
434
435 #[test]
436 fn test_complex_logical_expression() {
437 let expr = Expression::And {
439 left: Box::new(Expression::IsNotNull {
440 operand: Box::new(Expression::FieldAccess {
441 object: Box::new(Expression::Param { index: 0 }),
442 class_name: "Shift".into(),
443 field_name: "employee".into(),
444 }),
445 }),
446 right: Box::new(Expression::Eq {
447 left: Box::new(Expression::FieldAccess {
448 object: Box::new(Expression::Param { index: 0 }),
449 class_name: "Employee".into(),
450 field_name: "skill".into(),
451 }),
452 right: Box::new(Expression::IntLiteral { value: 42 }), }),
454 };
455
456 let json = serde_json::to_string(&expr).unwrap();
458 let deserialized: Expression = serde_json::from_str(&json).unwrap();
459 assert_eq!(expr, deserialized);
460 }
461
462 #[test]
463 fn test_time_calculation_expression() {
464 let expr = Expression::Div {
466 left: Box::new(Expression::FieldAccess {
467 object: Box::new(Expression::Param { index: 0 }),
468 class_name: "Shift".into(),
469 field_name: "start".into(),
470 }),
471 right: Box::new(Expression::IntLiteral { value: 24 }),
472 };
473
474 let json = serde_json::to_string(&expr).unwrap();
476 let deserialized: Expression = serde_json::from_str(&json).unwrap();
477 assert_eq!(expr, deserialized);
478 }
479
480 #[test]
483 fn test_host_call() {
484 let expr = Expression::HostCall {
485 function_name: "hstringEquals".into(),
486 args: vec![
487 Expression::FieldAccess {
488 object: Box::new(Expression::Param { index: 0 }),
489 class_name: "Employee".into(),
490 field_name: "skill".into(),
491 },
492 Expression::FieldAccess {
493 object: Box::new(Expression::Param { index: 1 }),
494 class_name: "Shift".into(),
495 field_name: "requiredSkill".into(),
496 },
497 ],
498 };
499
500 match expr {
501 Expression::HostCall {
502 function_name,
503 args,
504 } => {
505 assert_eq!(function_name, "hstringEquals");
506 assert_eq!(args.len(), 2);
507 }
508 _ => panic!("Expected HostCall"),
509 }
510 }
511
512 #[test]
513 fn test_serialize_host_call() {
514 let expr = Expression::HostCall {
515 function_name: "hstringEquals".into(),
516 args: vec![
517 Expression::IntLiteral { value: 1 },
518 Expression::IntLiteral { value: 2 },
519 ],
520 };
521
522 let json = serde_json::to_string(&expr).unwrap();
523 let deserialized: Expression = serde_json::from_str(&json).unwrap();
524 assert_eq!(expr, deserialized);
525 }
526
527 #[test]
528 fn test_host_call_with_no_args() {
529 let expr = Expression::HostCall {
530 function_name: "hnewList".into(),
531 args: vec![],
532 };
533
534 let json = serde_json::to_string(&expr).unwrap();
535 let deserialized: Expression = serde_json::from_str(&json).unwrap();
536 assert_eq!(expr, deserialized);
537 }
538
539 #[test]
540 fn test_complex_host_call_expression() {
541 let expr = Expression::And {
544 left: Box::new(Expression::IsNotNull {
545 operand: Box::new(Expression::FieldAccess {
546 object: Box::new(Expression::Param { index: 0 }),
547 class_name: "Shift".into(),
548 field_name: "employee".into(),
549 }),
550 }),
551 right: Box::new(Expression::HostCall {
552 function_name: "hstringEquals".into(),
553 args: vec![
554 Expression::FieldAccess {
555 object: Box::new(Expression::Param { index: 0 }),
556 class_name: "Employee".into(),
557 field_name: "skill".into(),
558 },
559 Expression::FieldAccess {
560 object: Box::new(Expression::Param { index: 1 }),
561 class_name: "Shift".into(),
562 field_name: "requiredSkill".into(),
563 },
564 ],
565 }),
566 };
567
568 let json = serde_json::to_string(&expr).unwrap();
570 let deserialized: Expression = serde_json::from_str(&json).unwrap();
571 assert_eq!(expr, deserialized);
572 }
573
574 #[test]
575 fn test_list_contains() {
576 let expr = Expression::ListContains {
577 list: Box::new(Expression::FieldAccess {
578 object: Box::new(Expression::Param { index: 0 }),
579 class_name: "Employee".into(),
580 field_name: "skills".into(),
581 }),
582 element: Box::new(Expression::FieldAccess {
583 object: Box::new(Expression::Param { index: 1 }),
584 class_name: "Shift".into(),
585 field_name: "requiredSkill".into(),
586 }),
587 };
588
589 match &expr {
590 Expression::ListContains { list, element } => {
591 assert!(matches!(
592 **list,
593 Expression::FieldAccess {
594 field_name: ref name,
595 ..
596 } if name == "skills"
597 ));
598 assert!(matches!(
599 **element,
600 Expression::FieldAccess {
601 field_name: ref name,
602 ..
603 } if name == "requiredSkill"
604 ));
605 }
606 _ => panic!("Expected ListContains expression"),
607 }
608
609 let json = serde_json::to_string(&expr).unwrap();
611 let deserialized: Expression = serde_json::from_str(&json).unwrap();
612 assert_eq!(expr, deserialized);
613 }
614}