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