use std::cmp::Ordering;
use crate::parser::ast::Value;
#[must_use]
pub fn compare_values(left: &Value, right: &Value) -> Option<Ordering> {
match (left, right) {
(Value::Uint(a), Value::Uint(b)) => Some(a.cmp(b)),
(Value::Int(a), Value::Int(b)) => Some(a.cmp(b)),
(Value::Uint(a), Value::Int(b)) => Some(i128::from(*a).cmp(&i128::from(*b))),
(Value::Int(a), Value::Uint(b)) => Some(i128::from(*a).cmp(&i128::from(*b))),
(Value::Float(a), Value::Float(b)) => a.partial_cmp(b),
(Value::String(a), Value::String(b)) => Some(a.cmp(b)),
(Value::Bytes(a), Value::Bytes(b)) => Some(a.cmp(b)),
_ => None,
}
}
#[must_use]
pub fn apply_less_than(left: &Value, right: &Value) -> bool {
compare_values(left, right) == Some(Ordering::Less)
}
#[must_use]
pub fn apply_greater_than(left: &Value, right: &Value) -> bool {
compare_values(left, right) == Some(Ordering::Greater)
}
#[must_use]
pub fn apply_less_equal(left: &Value, right: &Value) -> bool {
matches!(
compare_values(left, right),
Some(Ordering::Less | Ordering::Equal)
)
}
#[must_use]
pub fn apply_greater_equal(left: &Value, right: &Value) -> bool {
matches!(
compare_values(left, right),
Some(Ordering::Greater | Ordering::Equal)
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compare_values_ordering() {
use std::cmp::Ordering::*;
assert_eq!(
compare_values(&Value::Uint(5), &Value::Uint(10)),
Some(Less)
);
assert_eq!(
compare_values(&Value::Uint(10), &Value::Uint(10)),
Some(Equal)
);
assert_eq!(
compare_values(&Value::Uint(10), &Value::Uint(5)),
Some(Greater)
);
assert_eq!(
compare_values(&Value::Int(-10), &Value::Int(-5)),
Some(Less)
);
assert_eq!(
compare_values(&Value::Int(i64::MIN), &Value::Int(0)),
Some(Less)
);
assert_eq!(compare_values(&Value::Int(-1), &Value::Uint(0)), Some(Less));
assert_eq!(
compare_values(&Value::Uint(42), &Value::Int(42)),
Some(Equal)
);
assert_eq!(
compare_values(&Value::Uint(u64::MAX), &Value::Int(-1)),
Some(Greater)
);
assert_eq!(
compare_values(&Value::String("abc".into()), &Value::String("abd".into())),
Some(Less)
);
assert_eq!(
compare_values(&Value::String("abc".into()), &Value::String("abc".into())),
Some(Equal)
);
assert_eq!(
compare_values(&Value::Bytes(vec![1]), &Value::Bytes(vec![2])),
Some(Less)
);
assert_eq!(
compare_values(&Value::Bytes(vec![1]), &Value::Bytes(vec![1])),
Some(Equal)
);
assert_eq!(
compare_values(&Value::Bytes(vec![1]), &Value::Bytes(vec![1, 2])),
Some(Less)
);
assert_eq!(
compare_values(&Value::Bytes(vec![]), &Value::Bytes(vec![1])),
Some(Less)
);
assert_eq!(
compare_values(&Value::Uint(1), &Value::String("1".into())),
None
);
assert_eq!(compare_values(&Value::Int(1), &Value::Bytes(vec![1])), None);
}
#[test]
fn test_compare_values_float_ordering() {
use std::cmp::Ordering::*;
assert_eq!(
compare_values(&Value::Float(1.0), &Value::Float(2.0)),
Some(Less)
);
assert_eq!(
compare_values(&Value::Float(2.0), &Value::Float(2.0)),
Some(Equal)
);
assert_eq!(
compare_values(&Value::Float(3.0), &Value::Float(2.0)),
Some(Greater)
);
assert_eq!(
compare_values(&Value::Float(-1.0), &Value::Float(1.0)),
Some(Less)
);
assert_eq!(
compare_values(&Value::Float(1.0), &Value::Float(f64::INFINITY)),
Some(Less)
);
assert_eq!(
compare_values(&Value::Float(f64::NEG_INFINITY), &Value::Float(1.0)),
Some(Less)
);
assert_eq!(
compare_values(&Value::Float(f64::INFINITY), &Value::Float(f64::INFINITY)),
Some(Equal)
);
assert_eq!(
compare_values(&Value::Float(f64::NAN), &Value::Float(1.0)),
None
);
assert_eq!(
compare_values(&Value::Float(1.0), &Value::Float(f64::NAN)),
None
);
assert_eq!(
compare_values(&Value::Float(f64::NAN), &Value::Float(f64::NAN)),
None
);
assert_eq!(compare_values(&Value::Float(1.0), &Value::Uint(1)), None);
assert_eq!(compare_values(&Value::Int(1), &Value::Float(1.0)), None);
}
#[test]
fn test_comparison_operators_float() {
assert!(apply_less_than(&Value::Float(1.0), &Value::Float(2.0)));
assert!(!apply_less_than(&Value::Float(2.0), &Value::Float(2.0)));
assert!(apply_greater_than(&Value::Float(3.0), &Value::Float(2.0)));
assert!(!apply_greater_than(&Value::Float(2.0), &Value::Float(2.0)));
assert!(apply_less_equal(&Value::Float(2.0), &Value::Float(2.0)));
assert!(apply_less_equal(&Value::Float(1.0), &Value::Float(2.0)));
assert!(apply_greater_equal(&Value::Float(2.0), &Value::Float(2.0)));
assert!(apply_greater_equal(&Value::Float(3.0), &Value::Float(2.0)));
assert!(!apply_less_than(
&Value::Float(f64::NAN),
&Value::Float(1.0)
));
assert!(!apply_greater_than(
&Value::Float(f64::NAN),
&Value::Float(1.0)
));
assert!(!apply_less_equal(
&Value::Float(f64::NAN),
&Value::Float(1.0)
));
assert!(!apply_greater_equal(
&Value::Float(f64::NAN),
&Value::Float(1.0)
));
}
#[test]
fn test_comparison_operators_consistency() {
let pairs = vec![
(Value::Uint(5), Value::Uint(10)),
(Value::Uint(10), Value::Uint(10)),
(Value::Uint(10), Value::Uint(5)),
(Value::Int(-10), Value::Int(-5)),
(Value::Int(-1), Value::Uint(0)),
(Value::Uint(u64::MAX), Value::Int(-1)),
(Value::String("abc".into()), Value::String("abd".into())),
(Value::Bytes(vec![1, 2]), Value::Bytes(vec![1, 3])),
(Value::Bytes(vec![1]), Value::Bytes(vec![1, 2])),
(Value::Uint(1), Value::String("1".into())), ];
for (left, right) in &pairs {
let ord = compare_values(left, right);
assert_eq!(
apply_less_than(left, right),
ord == Some(Ordering::Less),
"< for {left:?}, {right:?}"
);
assert_eq!(
apply_greater_than(left, right),
ord == Some(Ordering::Greater),
"> for {left:?}, {right:?}"
);
assert_eq!(
apply_less_equal(left, right),
matches!(ord, Some(Ordering::Less | Ordering::Equal)),
"<= for {left:?}, {right:?}"
);
assert_eq!(
apply_greater_equal(left, right),
matches!(ord, Some(Ordering::Greater | Ordering::Equal)),
">= for {left:?}, {right:?}"
);
}
}
}