1use crate::evaluation::OperationResult;
4use crate::planning::semantics::{
5 primitive_boolean, ComparisonComputation, LiteralValue, SemanticConversionTarget, ValueKind,
6};
7use rust_decimal::Decimal;
8
9pub fn comparison_operation(
11 left: &LiteralValue,
12 op: &ComparisonComputation,
13 right: &LiteralValue,
14) -> OperationResult {
15 match (&left.value, &right.value) {
16 (ValueKind::Number(l), ValueKind::Number(r)) => {
17 OperationResult::Value(Box::new(LiteralValue::from_bool(compare_decimals(*l, op, r))))
18 }
19
20 (ValueKind::Boolean(l), ValueKind::Boolean(r)) => match op {
21 ComparisonComputation::Equal | ComparisonComputation::Is => {
22 OperationResult::Value(Box::new(LiteralValue::from_bool(l == r)))
23 }
24 ComparisonComputation::NotEqual | ComparisonComputation::IsNot => {
25 OperationResult::Value(Box::new(LiteralValue {
26 value: ValueKind::Boolean(l != r),
27 lemma_type: primitive_boolean().clone(),
28 }))
29 }
30 _ => unreachable!(
31 "BUG: invalid boolean comparison operator {}; this should be rejected during planning",
32 op
33 ),
34 },
35
36 (ValueKind::Text(l), ValueKind::Text(r)) => match op {
37 ComparisonComputation::Equal | ComparisonComputation::Is => {
38 OperationResult::Value(Box::new(LiteralValue::from_bool(l == r)))
39 }
40 ComparisonComputation::NotEqual | ComparisonComputation::IsNot => {
41 OperationResult::Value(Box::new(LiteralValue {
42 value: ValueKind::Boolean(l != r),
43 lemma_type: primitive_boolean().clone(),
44 }))
45 }
46 _ => unreachable!(
47 "BUG: invalid text comparison operator {}; this should be rejected during planning",
48 op
49 ),
50 },
51
52 (ValueKind::Ratio(l, _), ValueKind::Ratio(r, _)) => {
53 OperationResult::Value(Box::new(LiteralValue::from_bool(compare_decimals(*l, op, r))))
54 }
55 (ValueKind::Scale(l, lu), ValueKind::Scale(r, ru)) => {
56 if !left.lemma_type.same_scale_family(&right.lemma_type) {
57 unreachable!(
58 "BUG: compared different scale families ({} vs {}); this should be rejected during planning",
59 left.lemma_type.name(),
60 right.lemma_type.name()
61 );
62 }
63
64 if lu.eq_ignore_ascii_case(ru) {
65 return OperationResult::Value(Box::new(LiteralValue::from_bool(
66 compare_decimals(*l, op, r),
67 )));
68 }
69
70 let target = SemanticConversionTarget::ScaleUnit(lu.clone());
72 match super::units::convert_unit(right, &target) {
73 OperationResult::Value(converted) => match converted.as_ref().value {
74 ValueKind::Scale(converted_value, _) => OperationResult::Value(Box::new(
75 LiteralValue::from_bool(compare_decimals(*l, op, &converted_value)),
76 )),
77 _ => unreachable!("BUG: scale unit conversion returned non-scale value"),
78 },
79 OperationResult::Veto(msg) => {
80 unreachable!("BUG: scale unit conversion vetoed unexpectedly: {:?}", msg)
81 }
82 }
83 }
84
85 (ValueKind::Date(_), ValueKind::Date(_)) => super::datetime::datetime_comparison(left, op, right),
86 (ValueKind::Time(_), ValueKind::Time(_)) => super::datetime::time_comparison(left, op, right),
87
88 (ValueKind::Duration(l, lu), ValueKind::Duration(r, ru)) => {
90 let left_seconds = super::units::duration_to_seconds(*l, lu);
91 let right_seconds = super::units::duration_to_seconds(*r, ru);
92 OperationResult::Value(Box::new(LiteralValue::from_bool(
93 compare_decimals(left_seconds, op, &right_seconds),
94 )))
95 }
96
97 (ValueKind::Duration(value, _), ValueKind::Number(n)) => OperationResult::Value(Box::new(
99 LiteralValue::from_bool(compare_decimals(*value, op, n)),
100 )),
101 (ValueKind::Number(n), ValueKind::Duration(value, _)) => OperationResult::Value(Box::new(
102 LiteralValue::from_bool(compare_decimals(*n, op, value)),
103 )),
104
105 _ => unreachable!(
106 "BUG: unsupported comparison during evaluation: {} {} {}",
107 type_name(left),
108 op,
109 type_name(right)
110 ),
111 }
112}
113
114fn compare_decimals(left: Decimal, op: &ComparisonComputation, right: &Decimal) -> bool {
115 match op {
116 ComparisonComputation::GreaterThan => left > *right,
117 ComparisonComputation::LessThan => left < *right,
118 ComparisonComputation::GreaterThanOrEqual => left >= *right,
119 ComparisonComputation::LessThanOrEqual => left <= *right,
120 ComparisonComputation::Equal | ComparisonComputation::Is => left == *right,
121 ComparisonComputation::NotEqual | ComparisonComputation::IsNot => left != *right,
122 }
123}
124
125fn type_name(value: &LiteralValue) -> String {
126 value.get_type().name().to_string()
127}