1use serde::{
12 Deserialize,
13 Serialize,
14};
15use std::cmp::Ordering;
16
17use serde_json::{
18 Number,
19 Value,
20};
21
22use crate::Metadata;
23
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26pub enum Condition {
27 Equal {
29 key: String,
31 value: Value,
33 },
34 NotEqual {
36 key: String,
38 value: Value,
40 },
41 Less {
43 key: String,
45 value: Value,
47 },
48 LessEqual {
50 key: String,
52 value: Value,
54 },
55 Greater {
57 key: String,
59 value: Value,
61 },
62 GreaterEqual {
64 key: String,
66 value: Value,
68 },
69 In {
71 key: String,
73 values: Vec<Value>,
75 },
76 NotIn {
78 key: String,
80 values: Vec<Value>,
82 },
83 Exists {
85 key: String,
87 },
88 NotExists {
90 key: String,
92 },
93}
94
95impl Condition {
96 #[inline]
97 pub(crate) fn matches(&self, meta: &Metadata) -> bool {
98 match self {
99 Condition::Equal { key, value } => meta.get_raw(key) == Some(value),
100 Condition::NotEqual { key, value } => meta.get_raw(key) != Some(value),
101 Condition::Less { key, value } => meta
102 .get_raw(key)
103 .is_some_and(|v| compare_values(v, value) == Some(Ordering::Less)),
104 Condition::LessEqual { key, value } => meta.get_raw(key).is_some_and(|v| {
105 matches!(
106 compare_values(v, value),
107 Some(Ordering::Less) | Some(Ordering::Equal)
108 )
109 }),
110 Condition::Greater { key, value } => meta
111 .get_raw(key)
112 .is_some_and(|v| compare_values(v, value) == Some(Ordering::Greater)),
113 Condition::GreaterEqual { key, value } => meta.get_raw(key).is_some_and(|v| {
114 matches!(
115 compare_values(v, value),
116 Some(Ordering::Greater) | Some(Ordering::Equal)
117 )
118 }),
119 Condition::In { key, values } => meta.get_raw(key).is_some_and(|v| values.contains(v)),
120 Condition::NotIn { key, values } => {
121 meta.get_raw(key).map_or(true, |v| !values.contains(v))
122 }
123 Condition::Exists { key } => meta.contains_key(key),
124 Condition::NotExists { key } => !meta.contains_key(key),
125 }
126 }
127}
128
129#[inline]
132fn compare_values(a: &Value, b: &Value) -> Option<Ordering> {
133 match (a, b) {
134 (Value::Number(x), Value::Number(y)) => compare_numbers(x, y),
135 (Value::String(x), Value::String(y)) => x.partial_cmp(y),
136 _ => None,
137 }
138}
139
140const MAX_SAFE_INTEGER_F64_U64: u64 = 9_007_199_254_740_992; const I64_MIN_F64: f64 = -9_223_372_036_854_775_808.0; const I64_EXCLUSIVE_MAX_F64: f64 = 9_223_372_036_854_775_808.0; const U64_EXCLUSIVE_MAX_F64: f64 = 18_446_744_073_709_551_616.0; fn compare_numbers(a: &Number, b: &Number) -> Option<Ordering> {
146 if let (Some(xi), Some(yi)) = (a.as_i64(), b.as_i64()) {
147 return Some(xi.cmp(&yi));
148 }
149 if let (Some(xi), Some(yu)) = (a.as_i64(), b.as_u64()) {
150 return Some(compare_i64_u64(xi, yu));
151 }
152 if let (Some(xu), Some(yi)) = (a.as_u64(), b.as_i64()) {
153 return Some(compare_i64_u64(yi, xu).reverse());
154 }
155 if let (Some(xu), Some(yu)) = (a.as_u64(), b.as_u64()) {
156 return Some(xu.cmp(&yu));
157 }
158 if let (Some(xi), Some(yf)) = (a.as_i64(), b.as_f64()) {
159 return compare_i64_f64(xi, yf);
160 }
161 if let (Some(xf), Some(yi)) = (a.as_f64(), b.as_i64()) {
162 return compare_i64_f64(yi, xf).map(Ordering::reverse);
163 }
164 if let (Some(xu), Some(yf)) = (a.as_u64(), b.as_f64()) {
165 return compare_u64_f64(xu, yf);
166 }
167 if let (Some(xf), Some(yu)) = (a.as_f64(), b.as_u64()) {
168 return compare_u64_f64(yu, xf).map(Ordering::reverse);
169 }
170 if let (Some(xf), Some(yf)) = (a.as_f64(), b.as_f64()) {
171 return xf.partial_cmp(&yf);
172 }
173
174 unreachable!("Number must be representable as i64/u64/f64")
176}
177
178#[inline]
179fn compare_i64_u64(x: i64, y: u64) -> Ordering {
180 if x < 0 {
181 Ordering::Less
182 } else {
183 (x as u64).cmp(&y)
184 }
185}
186
187#[inline]
188fn compare_i64_f64(x: i64, y: f64) -> Option<Ordering> {
189 if y.fract() == 0.0 && (I64_MIN_F64..I64_EXCLUSIVE_MAX_F64).contains(&y) {
190 return Some(x.cmp(&(y as i64)));
192 }
193
194 if x.unsigned_abs() <= MAX_SAFE_INTEGER_F64_U64 {
195 return (x as f64).partial_cmp(&y);
196 }
197
198 None
199}
200
201#[inline]
202fn compare_u64_f64(x: u64, y: f64) -> Option<Ordering> {
203 if y < 0.0 {
204 return Some(Ordering::Greater);
205 }
206
207 if y.fract() == 0.0 && (0.0..U64_EXCLUSIVE_MAX_F64).contains(&y) {
208 return Some(x.cmp(&(y as u64)));
210 }
211
212 None
213}