grafeo_core/execution/operators/
value_utils.rs1use std::cmp::Ordering;
7
8use grafeo_common::types::Value;
9
10pub fn value_to_f64(value: &Value) -> Option<f64> {
14 match value {
15 Value::Int64(i) => Some(*i as f64),
16 Value::Float64(f) => Some(*f),
17 Value::String(s) => s.parse::<f64>().ok(),
19 _ => None,
20 }
21}
22
23pub fn compare_values(a: &Value, b: &Value) -> Option<Ordering> {
28 match (a, b) {
29 (Value::Int64(a), Value::Int64(b)) => Some(a.cmp(b)),
30 (Value::Float64(a), Value::Float64(b)) => a.partial_cmp(b),
31 (Value::String(a), Value::String(b)) => {
32 if let (Ok(a_num), Ok(b_num)) = (a.parse::<f64>(), b.parse::<f64>()) {
34 a_num.partial_cmp(&b_num)
35 } else {
36 Some(a.cmp(b))
37 }
38 }
39 (Value::Bool(a), Value::Bool(b)) => Some(a.cmp(b)),
40 (Value::Int64(a), Value::Float64(b)) => (*a as f64).partial_cmp(b),
41 (Value::Float64(a), Value::Int64(b)) => a.partial_cmp(&(*b as f64)),
42 (Value::String(s), Value::Int64(i)) => s.parse::<f64>().ok()?.partial_cmp(&(*i as f64)),
44 (Value::String(s), Value::Float64(f)) => s.parse::<f64>().ok()?.partial_cmp(f),
45 (Value::Int64(i), Value::String(s)) => (*i as f64).partial_cmp(&s.parse::<f64>().ok()?),
46 (Value::Float64(f), Value::String(s)) => f.partial_cmp(&s.parse::<f64>().ok()?),
47 _ => None,
48 }
49}
50
51pub fn compare_values_total(a: &Value, b: &Value) -> Ordering {
55 match (a, b) {
56 (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
57 (Value::Int64(a), Value::Int64(b)) => a.cmp(b),
58 (Value::Float64(a), Value::Float64(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
59 (Value::String(a), Value::String(b)) => a.cmp(b),
60 (Value::Int64(a), Value::Float64(b)) => {
61 (*a as f64).partial_cmp(b).unwrap_or(Ordering::Equal)
62 }
63 (Value::Float64(a), Value::Int64(b)) => {
64 a.partial_cmp(&(*b as f64)).unwrap_or(Ordering::Equal)
65 }
66 _ => Ordering::Equal,
67 }
68}
69
70pub fn is_less_than(current: &Option<Value>, new: &Value) -> bool {
74 match current {
75 None => true,
76 Some(curr) => compare_values(new, curr) == Some(Ordering::Less),
77 }
78}
79
80pub fn is_greater_than(current: &Option<Value>, new: &Value) -> bool {
84 match current {
85 None => true,
86 Some(curr) => compare_values(new, curr) == Some(Ordering::Greater),
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn value_to_f64_int() {
96 assert_eq!(value_to_f64(&Value::Int64(42)), Some(42.0));
97 }
98
99 #[test]
100 fn value_to_f64_float() {
101 assert_eq!(value_to_f64(&Value::Float64(2.72)), Some(2.72));
102 }
103
104 #[test]
105 fn value_to_f64_numeric_string() {
106 assert_eq!(value_to_f64(&Value::String("2.5".into())), Some(2.5));
107 }
108
109 #[test]
110 fn value_to_f64_non_numeric_string() {
111 assert_eq!(value_to_f64(&Value::String("abc".into())), None);
112 }
113
114 #[test]
115 fn value_to_f64_null() {
116 assert_eq!(value_to_f64(&Value::Null), None);
117 }
118
119 #[test]
120 fn compare_same_type_int() {
121 assert_eq!(
122 compare_values(&Value::Int64(1), &Value::Int64(2)),
123 Some(Ordering::Less)
124 );
125 }
126
127 #[test]
128 fn compare_cross_type_int_float() {
129 assert_eq!(
130 compare_values(&Value::Int64(2), &Value::Float64(2.0)),
131 Some(Ordering::Equal)
132 );
133 }
134
135 #[test]
136 fn compare_rdf_numeric_strings() {
137 assert_eq!(
138 compare_values(&Value::String("10".into()), &Value::String("9".into())),
139 Some(Ordering::Greater)
140 );
141 }
142
143 #[test]
144 fn compare_incomparable() {
145 assert_eq!(compare_values(&Value::Bool(true), &Value::Int64(1)), None);
146 }
147
148 #[test]
149 fn total_ordering_incomparable_returns_equal() {
150 assert_eq!(
151 compare_values_total(&Value::Bool(true), &Value::Int64(1)),
152 Ordering::Equal
153 );
154 }
155
156 #[test]
157 fn is_less_than_none_always_true() {
158 assert!(is_less_than(&None, &Value::Int64(5)));
159 }
160
161 #[test]
162 fn is_less_than_smaller() {
163 assert!(is_less_than(&Some(Value::Int64(10)), &Value::Int64(5)));
164 }
165
166 #[test]
167 fn is_less_than_larger() {
168 assert!(!is_less_than(&Some(Value::Int64(3)), &Value::Int64(5)));
169 }
170
171 #[test]
172 fn is_greater_than_none_always_true() {
173 assert!(is_greater_than(&None, &Value::Int64(5)));
174 }
175
176 #[test]
177 fn is_greater_than_larger() {
178 assert!(is_greater_than(&Some(Value::Int64(3)), &Value::Int64(5)));
179 }
180
181 #[test]
182 fn is_greater_than_smaller() {
183 assert!(!is_greater_than(&Some(Value::Int64(10)), &Value::Int64(5)));
184 }
185}