1use std::cmp::Ordering;
13
14use bigdecimal::BigDecimal;
15use num_bigint::BigInt;
16use qubit_value::Value;
17use serde::{
18 Deserialize,
19 Deserializer,
20 Serialize,
21 Serializer,
22};
23
24use super::missing_key_policy::MissingKeyPolicy;
25use super::number_comparison_policy::NumberComparisonPolicy;
26use super::wire::ConditionWire;
27use crate::Metadata;
28
29#[derive(Debug, Clone, PartialEq)]
31pub enum Condition {
32 Equal {
34 key: String,
36 value: Value,
38 },
39 NotEqual {
41 key: String,
43 value: Value,
45 },
46 Less {
48 key: String,
50 value: Value,
52 },
53 LessEqual {
55 key: String,
57 value: Value,
59 },
60 Greater {
62 key: String,
64 value: Value,
66 },
67 GreaterEqual {
69 key: String,
71 value: Value,
73 },
74 In {
76 key: String,
78 values: Vec<Value>,
80 },
81 NotIn {
83 key: String,
85 values: Vec<Value>,
87 },
88 Exists {
90 key: String,
92 },
93 NotExists {
95 key: String,
97 },
98}
99
100impl Serialize for Condition {
101 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
102 where
103 S: Serializer,
104 {
105 ConditionWire::from(self).serialize(serializer)
106 }
107}
108
109impl<'de> Deserialize<'de> for Condition {
110 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
111 where
112 D: Deserializer<'de>,
113 {
114 Ok(ConditionWire::deserialize(deserializer)?.into_condition())
115 }
116}
117
118impl Condition {
119 #[inline]
121 pub(crate) fn matches(
122 &self,
123 meta: &Metadata,
124 missing_key_policy: MissingKeyPolicy,
125 number_comparison_policy: NumberComparisonPolicy,
126 ) -> bool {
127 match self {
128 Condition::Equal { key, value } => meta
129 .get_raw(key)
130 .is_some_and(|stored| values_equal(stored, value, number_comparison_policy)),
131 Condition::NotEqual { key, value } => match meta.get_raw(key) {
132 Some(stored) => !values_equal(stored, value, number_comparison_policy),
133 None => missing_key_policy.matches_negative_predicates(),
134 },
135 Condition::Less { key, value } => meta
136 .get_raw(key)
137 .is_some_and(|stored| compare_values(stored, value, number_comparison_policy) == Some(Ordering::Less)),
138 Condition::LessEqual { key, value } => meta.get_raw(key).is_some_and(|stored| {
139 matches!(
140 compare_values(stored, value, number_comparison_policy),
141 Some(Ordering::Less) | Some(Ordering::Equal)
142 )
143 }),
144 Condition::Greater { key, value } => meta.get_raw(key).is_some_and(|stored| {
145 compare_values(stored, value, number_comparison_policy) == Some(Ordering::Greater)
146 }),
147 Condition::GreaterEqual { key, value } => meta.get_raw(key).is_some_and(|stored| {
148 matches!(
149 compare_values(stored, value, number_comparison_policy),
150 Some(Ordering::Greater) | Some(Ordering::Equal)
151 )
152 }),
153 Condition::In { key, values } => meta.get_raw(key).is_some_and(|stored| {
154 values
155 .iter()
156 .any(|value| values_equal(stored, value, number_comparison_policy))
157 }),
158 Condition::NotIn { key, values } => match meta.get_raw(key) {
159 Some(stored) => values
160 .iter()
161 .all(|value| !values_equal(stored, value, number_comparison_policy)),
162 None => missing_key_policy.matches_negative_predicates(),
163 },
164 Condition::Exists { key } => meta.contains_key(key),
165 Condition::NotExists { key } => !meta.contains_key(key),
166 }
167 }
168}
169
170#[inline]
172fn values_equal(a: &Value, b: &Value, number_comparison_policy: NumberComparisonPolicy) -> bool {
173 if is_numeric_value(a) && is_numeric_value(b) {
174 return compare_numbers(a, b, number_comparison_policy) == Some(Ordering::Equal);
175 }
176 a == b
177}
178
179#[inline]
181fn compare_values(a: &Value, b: &Value, number_comparison_policy: NumberComparisonPolicy) -> Option<Ordering> {
182 if is_numeric_value(a) && is_numeric_value(b) {
183 return compare_numbers(a, b, number_comparison_policy);
184 }
185 match (a, b) {
186 (Value::String(x), Value::String(y)) => x.partial_cmp(y),
187 _ => None,
188 }
189}
190
191#[derive(Debug, Clone, Copy)]
193enum NumberValue {
194 Signed(i128),
196 Unsigned(u128),
198 Float(f64),
200}
201
202#[inline]
204fn is_numeric_value(value: &Value) -> bool {
205 matches!(
206 value,
207 Value::Int8(_)
208 | Value::Int16(_)
209 | Value::Int32(_)
210 | Value::Int64(_)
211 | Value::Int128(_)
212 | Value::UInt8(_)
213 | Value::UInt16(_)
214 | Value::UInt32(_)
215 | Value::UInt64(_)
216 | Value::UInt128(_)
217 | Value::IntSize(_)
218 | Value::UIntSize(_)
219 | Value::Float32(_)
220 | Value::Float64(_)
221 | Value::BigInteger(_)
222 | Value::BigDecimal(_)
223 )
224}
225
226#[inline]
228fn number_value(value: &Value) -> Option<NumberValue> {
229 match value {
230 Value::Int8(v) => Some(NumberValue::Signed(i128::from(*v))),
231 Value::Int16(v) => Some(NumberValue::Signed(i128::from(*v))),
232 Value::Int32(v) => Some(NumberValue::Signed(i128::from(*v))),
233 Value::Int64(v) => Some(NumberValue::Signed(i128::from(*v))),
234 Value::Int128(v) => Some(NumberValue::Signed(*v)),
235 Value::UInt8(v) => Some(NumberValue::Unsigned(u128::from(*v))),
236 Value::UInt16(v) => Some(NumberValue::Unsigned(u128::from(*v))),
237 Value::UInt32(v) => Some(NumberValue::Unsigned(u128::from(*v))),
238 Value::UInt64(v) => Some(NumberValue::Unsigned(u128::from(*v))),
239 Value::UInt128(v) => Some(NumberValue::Unsigned(*v)),
240 Value::IntSize(v) => Some(NumberValue::Signed(*v as i128)),
241 Value::UIntSize(v) => Some(NumberValue::Unsigned(*v as u128)),
242 Value::Float32(v) => Some(NumberValue::Float(f64::from(*v))),
243 Value::Float64(v) => Some(NumberValue::Float(*v)),
244 _ => None,
245 }
246}
247
248#[inline]
250fn compare_numbers(a: &Value, b: &Value, number_comparison_policy: NumberComparisonPolicy) -> Option<Ordering> {
251 if contains_big_number(a, b) {
252 return compare_big_numbers(a, b, number_comparison_policy);
253 }
254 match (number_value(a)?, number_value(b)?) {
255 (NumberValue::Signed(x), NumberValue::Signed(y)) => Some(x.cmp(&y)),
256 (NumberValue::Unsigned(x), NumberValue::Unsigned(y)) => Some(x.cmp(&y)),
257 (NumberValue::Signed(x), NumberValue::Unsigned(y)) => Some(compare_i128_u128(x, y)),
258 (NumberValue::Unsigned(x), NumberValue::Signed(y)) => Some(compare_i128_u128(y, x).reverse()),
259 (NumberValue::Signed(x), NumberValue::Float(y)) => compare_i128_f64(x, y, number_comparison_policy),
260 (NumberValue::Float(x), NumberValue::Signed(y)) => {
261 compare_i128_f64(y, x, number_comparison_policy).map(Ordering::reverse)
262 }
263 (NumberValue::Unsigned(x), NumberValue::Float(y)) => compare_u128_f64(x, y, number_comparison_policy),
264 (NumberValue::Float(x), NumberValue::Unsigned(y)) => {
265 compare_u128_f64(y, x, number_comparison_policy).map(Ordering::reverse)
266 }
267 (NumberValue::Float(x), NumberValue::Float(y)) => x.partial_cmp(&y),
268 }
269}
270
271#[inline]
273fn contains_big_number(a: &Value, b: &Value) -> bool {
274 matches!(a, Value::BigInteger(_) | Value::BigDecimal(_)) || matches!(b, Value::BigInteger(_) | Value::BigDecimal(_))
275}
276
277fn compare_big_numbers(a: &Value, b: &Value, number_comparison_policy: NumberComparisonPolicy) -> Option<Ordering> {
279 if let (Some(x), Some(y)) = (big_integer_value(a), big_integer_value(b)) {
280 return Some(x.cmp(&y));
281 }
282 if let (Some(x), Some(y)) = (big_decimal_value(a), big_decimal_value(b)) {
283 return Some(x.cmp(&y));
284 }
285 if matches!(number_comparison_policy, NumberComparisonPolicy::Approximate) {
286 return compare_as_f64(a, b);
287 }
288 None
289}
290
291fn big_integer_value(value: &Value) -> Option<BigInt> {
293 match value {
294 Value::Int8(v) => Some(BigInt::from(*v)),
295 Value::Int16(v) => Some(BigInt::from(*v)),
296 Value::Int32(v) => Some(BigInt::from(*v)),
297 Value::Int64(v) => Some(BigInt::from(*v)),
298 Value::Int128(v) => Some(BigInt::from(*v)),
299 Value::UInt8(v) => Some(BigInt::from(*v)),
300 Value::UInt16(v) => Some(BigInt::from(*v)),
301 Value::UInt32(v) => Some(BigInt::from(*v)),
302 Value::UInt64(v) => Some(BigInt::from(*v)),
303 Value::UInt128(v) => Some(BigInt::from(*v)),
304 Value::IntSize(v) => Some(BigInt::from(*v)),
305 Value::UIntSize(v) => Some(BigInt::from(*v)),
306 Value::BigInteger(v) => Some(v.clone()),
307 _ => None,
308 }
309}
310
311fn big_decimal_value(value: &Value) -> Option<BigDecimal> {
313 match value {
314 Value::BigDecimal(v) => Some(v.clone()),
315 _ => big_integer_value(value).map(BigDecimal::from),
316 }
317}
318
319#[inline]
321fn compare_as_f64(a: &Value, b: &Value) -> Option<Ordering> {
322 a.to::<f64>().ok()?.partial_cmp(&b.to::<f64>().ok()?)
323}
324
325const MAX_SAFE_INTEGER_F64_U64: u64 = 9_007_199_254_740_992;
326const I64_MIN_F64: f64 = -9_223_372_036_854_775_808.0;
327const I64_EXCLUSIVE_MAX_F64: f64 = 9_223_372_036_854_775_808.0;
328const U64_EXCLUSIVE_MAX_F64: f64 = 18_446_744_073_709_551_616.0;
329
330#[inline]
332fn compare_i128_u128(x: i128, y: u128) -> Ordering {
333 if x < 0 { Ordering::Less } else { (x as u128).cmp(&y) }
334}
335
336fn compare_i128_f64(x: i128, y: f64, number_comparison_policy: NumberComparisonPolicy) -> Option<Ordering> {
338 if let Ok(x64) = i64::try_from(x) {
339 return compare_i64_f64(x64, y, number_comparison_policy);
340 }
341 if matches!(number_comparison_policy, NumberComparisonPolicy::Approximate) {
342 return (x as f64).partial_cmp(&y);
343 }
344 None
345}
346
347fn compare_u128_f64(x: u128, y: f64, number_comparison_policy: NumberComparisonPolicy) -> Option<Ordering> {
349 if let Ok(x64) = u64::try_from(x) {
350 return compare_u64_f64(x64, y, number_comparison_policy);
351 }
352 if matches!(number_comparison_policy, NumberComparisonPolicy::Approximate) {
353 return (x as f64).partial_cmp(&y);
354 }
355 None
356}
357
358fn compare_i64_f64(x: i64, y: f64, number_comparison_policy: NumberComparisonPolicy) -> Option<Ordering> {
360 if y.fract() == 0.0 && (I64_MIN_F64..I64_EXCLUSIVE_MAX_F64).contains(&y) {
361 return Some(x.cmp(&(y as i64)));
362 }
363
364 if x.unsigned_abs() <= MAX_SAFE_INTEGER_F64_U64 {
365 return (x as f64).partial_cmp(&y);
366 }
367
368 if matches!(number_comparison_policy, NumberComparisonPolicy::Approximate) {
369 return (x as f64).partial_cmp(&y);
370 }
371
372 None
373}
374
375fn compare_u64_f64(x: u64, y: f64, number_comparison_policy: NumberComparisonPolicy) -> Option<Ordering> {
377 if y < 0.0 {
378 return Some(Ordering::Greater);
379 }
380
381 if y.fract() == 0.0 && (0.0..U64_EXCLUSIVE_MAX_F64).contains(&y) {
382 return Some(x.cmp(&(y as u64)));
383 }
384
385 if x <= MAX_SAFE_INTEGER_F64_U64 {
386 return (x as f64).partial_cmp(&y);
387 }
388
389 if matches!(number_comparison_policy, NumberComparisonPolicy::Approximate) {
390 return (x as f64).partial_cmp(&y);
391 }
392
393 None
394}