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 pub(crate) fn matches(&self, meta: &Metadata) -> bool {
97 match self {
98 Condition::Equal { key, value } => meta.get_raw(key) == Some(value),
99 Condition::NotEqual { key, value } => meta.get_raw(key) != Some(value),
100 Condition::Less { key, value } => meta
101 .get_raw(key)
102 .is_some_and(|v| compare_values(v, value) == Some(Ordering::Less)),
103 Condition::LessEqual { key, value } => meta.get_raw(key).is_some_and(|v| {
104 matches!(
105 compare_values(v, value),
106 Some(Ordering::Less) | Some(Ordering::Equal)
107 )
108 }),
109 Condition::Greater { key, value } => meta
110 .get_raw(key)
111 .is_some_and(|v| compare_values(v, value) == Some(Ordering::Greater)),
112 Condition::GreaterEqual { key, value } => meta.get_raw(key).is_some_and(|v| {
113 matches!(
114 compare_values(v, value),
115 Some(Ordering::Greater) | Some(Ordering::Equal)
116 )
117 }),
118 Condition::In { key, values } => meta.get_raw(key).is_some_and(|v| values.contains(v)),
119 Condition::NotIn { key, values } => {
120 meta.get_raw(key).map_or(true, |v| !values.contains(v))
121 }
122 Condition::Exists { key } => meta.contains_key(key),
123 Condition::NotExists { key } => !meta.contains_key(key),
124 }
125 }
126}
127
128fn compare_values(a: &Value, b: &Value) -> Option<Ordering> {
131 match (a, b) {
132 (Value::Number(x), Value::Number(y)) => compare_numbers(x, y),
133 (Value::String(x), Value::String(y)) => x.partial_cmp(y),
134 _ => None,
135 }
136}
137
138const 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> {
144 if let (Some(xi), Some(yi)) = (a.as_i64(), b.as_i64()) {
145 return Some(xi.cmp(&yi));
146 }
147 if let (Some(xi), Some(yu)) = (a.as_i64(), b.as_u64()) {
148 return Some(compare_i64_u64(xi, yu));
149 }
150 if let (Some(xu), Some(yi)) = (a.as_u64(), b.as_i64()) {
151 return Some(compare_i64_u64(yi, xu).reverse());
152 }
153 if let (Some(xu), Some(yu)) = (a.as_u64(), b.as_u64()) {
154 return Some(xu.cmp(&yu));
155 }
156 if let (Some(xi), Some(yf)) = (a.as_i64(), b.as_f64()) {
157 return compare_i64_f64(xi, yf);
158 }
159 if let (Some(xf), Some(yi)) = (a.as_f64(), b.as_i64()) {
160 return compare_i64_f64(yi, xf).map(Ordering::reverse);
161 }
162 if let (Some(xu), Some(yf)) = (a.as_u64(), b.as_f64()) {
163 return compare_u64_f64(xu, yf);
164 }
165 if let (Some(xf), Some(yu)) = (a.as_f64(), b.as_u64()) {
166 return compare_u64_f64(yu, xf).map(Ordering::reverse);
167 }
168 if let (Some(xf), Some(yf)) = (a.as_f64(), b.as_f64()) {
169 return xf.partial_cmp(&yf);
170 }
171
172 unreachable!("Number must be representable as i64/u64/f64")
174}
175
176#[inline]
177fn compare_i64_u64(x: i64, y: u64) -> Ordering {
178 if x < 0 {
179 Ordering::Less
180 } else {
181 (x as u64).cmp(&y)
182 }
183}
184
185#[inline]
186fn compare_i64_f64(x: i64, y: f64) -> Option<Ordering> {
187 if y.fract() == 0.0 && (I64_MIN_F64..I64_EXCLUSIVE_MAX_F64).contains(&y) {
188 return Some(x.cmp(&(y as i64)));
190 }
191
192 if x.unsigned_abs() <= MAX_SAFE_INTEGER_F64_U64 {
193 return (x as f64).partial_cmp(&y);
194 }
195
196 None
197}
198
199#[inline]
200fn compare_u64_f64(x: u64, y: f64) -> Option<Ordering> {
201 if y < 0.0 {
202 return Some(Ordering::Greater);
203 }
204
205 if y.fract() == 0.0 && (0.0..U64_EXCLUSIVE_MAX_F64).contains(&y) {
206 return Some(x.cmp(&(y as u64)));
208 }
209
210 None
211}