1use std::cmp::Ordering;
7
8use crate::parser::ast::Value;
9
10#[must_use]
29pub fn compare_values(left: &Value, right: &Value) -> Option<Ordering> {
30 match (left, right) {
31 (Value::Uint(a), Value::Uint(b)) => Some(a.cmp(b)),
32 (Value::Int(a), Value::Int(b)) => Some(a.cmp(b)),
33 (Value::Uint(a), Value::Int(b)) => Some(i128::from(*a).cmp(&i128::from(*b))),
34 (Value::Int(a), Value::Uint(b)) => Some(i128::from(*a).cmp(&i128::from(*b))),
35 (Value::Float(a), Value::Float(b)) => a.partial_cmp(b),
36 (Value::String(a), Value::String(b)) => Some(a.cmp(b)),
37 (Value::Bytes(a), Value::Bytes(b)) => Some(a.cmp(b)),
38 (Value::String(s), Value::Bytes(b)) => Some(s.as_bytes().cmp(b.as_slice())),
46 (Value::Bytes(b), Value::String(s)) => Some(b.as_slice().cmp(s.as_bytes())),
47 _ => None,
48 }
49}
50
51#[must_use]
64pub fn apply_less_than(left: &Value, right: &Value) -> bool {
65 compare_values(left, right) == Some(Ordering::Less)
66}
67
68#[must_use]
81pub fn apply_greater_than(left: &Value, right: &Value) -> bool {
82 compare_values(left, right) == Some(Ordering::Greater)
83}
84
85#[must_use]
98pub fn apply_less_equal(left: &Value, right: &Value) -> bool {
99 matches!(
100 compare_values(left, right),
101 Some(Ordering::Less | Ordering::Equal)
102 )
103}
104
105#[must_use]
118pub fn apply_greater_equal(left: &Value, right: &Value) -> bool {
119 matches!(
120 compare_values(left, right),
121 Some(Ordering::Greater | Ordering::Equal)
122 )
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_compare_values_ordering() {
131 use std::cmp::Ordering::*;
132
133 assert_eq!(
135 compare_values(&Value::Uint(5), &Value::Uint(10)),
136 Some(Less)
137 );
138 assert_eq!(
139 compare_values(&Value::Uint(10), &Value::Uint(10)),
140 Some(Equal)
141 );
142 assert_eq!(
143 compare_values(&Value::Uint(10), &Value::Uint(5)),
144 Some(Greater)
145 );
146 assert_eq!(
147 compare_values(&Value::Int(-10), &Value::Int(-5)),
148 Some(Less)
149 );
150 assert_eq!(
151 compare_values(&Value::Int(i64::MIN), &Value::Int(0)),
152 Some(Less)
153 );
154
155 assert_eq!(compare_values(&Value::Int(-1), &Value::Uint(0)), Some(Less));
157 assert_eq!(
158 compare_values(&Value::Uint(42), &Value::Int(42)),
159 Some(Equal)
160 );
161 assert_eq!(
162 compare_values(&Value::Uint(u64::MAX), &Value::Int(-1)),
163 Some(Greater)
164 );
165
166 assert_eq!(
168 compare_values(&Value::String("abc".into()), &Value::String("abd".into())),
169 Some(Less)
170 );
171 assert_eq!(
172 compare_values(&Value::String("abc".into()), &Value::String("abc".into())),
173 Some(Equal)
174 );
175
176 assert_eq!(
178 compare_values(&Value::Bytes(vec![1]), &Value::Bytes(vec![2])),
179 Some(Less)
180 );
181 assert_eq!(
182 compare_values(&Value::Bytes(vec![1]), &Value::Bytes(vec![1])),
183 Some(Equal)
184 );
185 assert_eq!(
186 compare_values(&Value::Bytes(vec![1]), &Value::Bytes(vec![1, 2])),
187 Some(Less)
188 );
189 assert_eq!(
190 compare_values(&Value::Bytes(vec![]), &Value::Bytes(vec![1])),
191 Some(Less)
192 );
193
194 assert_eq!(
196 compare_values(&Value::Uint(1), &Value::String("1".into())),
197 None
198 );
199 assert_eq!(compare_values(&Value::Int(1), &Value::Bytes(vec![1])), None);
200 }
201
202 #[test]
203 fn test_compare_values_float_ordering() {
204 use std::cmp::Ordering::*;
205
206 assert_eq!(
207 compare_values(&Value::Float(1.0), &Value::Float(2.0)),
208 Some(Less)
209 );
210 assert_eq!(
211 compare_values(&Value::Float(2.0), &Value::Float(2.0)),
212 Some(Equal)
213 );
214 assert_eq!(
215 compare_values(&Value::Float(3.0), &Value::Float(2.0)),
216 Some(Greater)
217 );
218 assert_eq!(
219 compare_values(&Value::Float(-1.0), &Value::Float(1.0)),
220 Some(Less)
221 );
222
223 assert_eq!(
225 compare_values(&Value::Float(1.0), &Value::Float(f64::INFINITY)),
226 Some(Less)
227 );
228 assert_eq!(
229 compare_values(&Value::Float(f64::NEG_INFINITY), &Value::Float(1.0)),
230 Some(Less)
231 );
232 assert_eq!(
233 compare_values(&Value::Float(f64::INFINITY), &Value::Float(f64::INFINITY)),
234 Some(Equal)
235 );
236
237 assert_eq!(
239 compare_values(&Value::Float(f64::NAN), &Value::Float(1.0)),
240 None
241 );
242 assert_eq!(
243 compare_values(&Value::Float(1.0), &Value::Float(f64::NAN)),
244 None
245 );
246 assert_eq!(
247 compare_values(&Value::Float(f64::NAN), &Value::Float(f64::NAN)),
248 None
249 );
250
251 assert_eq!(compare_values(&Value::Float(1.0), &Value::Uint(1)), None);
253 assert_eq!(compare_values(&Value::Int(1), &Value::Float(1.0)), None);
254 }
255
256 #[test]
257 fn test_comparison_operators_float() {
258 assert!(apply_less_than(&Value::Float(1.0), &Value::Float(2.0)));
260 assert!(!apply_less_than(&Value::Float(2.0), &Value::Float(2.0)));
261 assert!(apply_greater_than(&Value::Float(3.0), &Value::Float(2.0)));
262 assert!(!apply_greater_than(&Value::Float(2.0), &Value::Float(2.0)));
263 assert!(apply_less_equal(&Value::Float(2.0), &Value::Float(2.0)));
264 assert!(apply_less_equal(&Value::Float(1.0), &Value::Float(2.0)));
265 assert!(apply_greater_equal(&Value::Float(2.0), &Value::Float(2.0)));
266 assert!(apply_greater_equal(&Value::Float(3.0), &Value::Float(2.0)));
267
268 assert!(!apply_less_than(
270 &Value::Float(f64::NAN),
271 &Value::Float(1.0)
272 ));
273 assert!(!apply_greater_than(
274 &Value::Float(f64::NAN),
275 &Value::Float(1.0)
276 ));
277 assert!(!apply_less_equal(
278 &Value::Float(f64::NAN),
279 &Value::Float(1.0)
280 ));
281 assert!(!apply_greater_equal(
282 &Value::Float(f64::NAN),
283 &Value::Float(1.0)
284 ));
285 }
286
287 #[test]
288 fn test_comparison_operators_consistency() {
289 let pairs = vec![
291 (Value::Uint(5), Value::Uint(10)),
292 (Value::Uint(10), Value::Uint(10)),
293 (Value::Uint(10), Value::Uint(5)),
294 (Value::Int(-10), Value::Int(-5)),
295 (Value::Int(-1), Value::Uint(0)),
296 (Value::Uint(u64::MAX), Value::Int(-1)),
297 (Value::String("abc".into()), Value::String("abd".into())),
298 (Value::Bytes(vec![1, 2]), Value::Bytes(vec![1, 3])),
299 (Value::Bytes(vec![1]), Value::Bytes(vec![1, 2])),
300 (Value::Uint(1), Value::String("1".into())), ];
302
303 for (left, right) in &pairs {
304 let ord = compare_values(left, right);
305 assert_eq!(
306 apply_less_than(left, right),
307 ord == Some(Ordering::Less),
308 "< for {left:?}, {right:?}"
309 );
310 assert_eq!(
311 apply_greater_than(left, right),
312 ord == Some(Ordering::Greater),
313 "> for {left:?}, {right:?}"
314 );
315 assert_eq!(
316 apply_less_equal(left, right),
317 matches!(ord, Some(Ordering::Less | Ordering::Equal)),
318 "<= for {left:?}, {right:?}"
319 );
320 assert_eq!(
321 apply_greater_equal(left, right),
322 matches!(ord, Some(Ordering::Greater | Ordering::Equal)),
323 ">= for {left:?}, {right:?}"
324 );
325 }
326 }
327
328 #[test]
337 fn cross_type_string_bytes_ordering_is_byte_sequence() {
338 assert_eq!(
340 compare_values(
341 &Value::String("abc".to_string()),
342 &Value::Bytes(b"abc".to_vec())
343 ),
344 Some(Ordering::Equal)
345 );
346 assert_eq!(
347 compare_values(
348 &Value::Bytes(b"abc".to_vec()),
349 &Value::String("abc".to_string())
350 ),
351 Some(Ordering::Equal)
352 );
353
354 assert_eq!(
356 compare_values(
357 &Value::Bytes(b"a".to_vec()),
358 &Value::String("b".to_string())
359 ),
360 Some(Ordering::Less)
361 );
362 assert_eq!(
363 compare_values(
364 &Value::String("z".to_string()),
365 &Value::Bytes(b"a".to_vec())
366 ),
367 Some(Ordering::Greater)
368 );
369
370 let pairs: &[(Value, Value)] = &[
373 (Value::String("abc".into()), Value::Bytes(b"abc".to_vec())),
374 (Value::String("abc".into()), Value::Bytes(b"abd".to_vec())),
375 (Value::Bytes(b"abc".to_vec()), Value::String("abb".into())),
376 ];
377 for (l, r) in pairs {
378 let lt = apply_less_than(l, r);
379 let eq = compare_values(l, r) == Some(Ordering::Equal);
380 let gt = apply_greater_than(l, r);
381 assert_eq!(
382 u8::from(lt) + u8::from(eq) + u8::from(gt),
383 1,
384 "trichotomy broken for {l:?} vs {r:?}: lt={lt} eq={eq} gt={gt}"
385 );
386 }
387 }
388}