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.get_raw(key).is_some_and(|stored| {
136 compare_values(stored, value, number_comparison_policy) == Some(Ordering::Less)
137 }),
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(
182 a: &Value,
183 b: &Value,
184 number_comparison_policy: NumberComparisonPolicy,
185) -> Option<Ordering> {
186 if is_numeric_value(a) && is_numeric_value(b) {
187 return compare_numbers(a, b, number_comparison_policy);
188 }
189 match (a, b) {
190 (Value::String(x), Value::String(y)) => x.partial_cmp(y),
191 _ => None,
192 }
193}
194
195#[derive(Debug, Clone, Copy)]
197enum NumberValue {
198 Signed(i128),
200 Unsigned(u128),
202 Float(f64),
204}
205
206#[inline]
208fn is_numeric_value(value: &Value) -> bool {
209 matches!(
210 value,
211 Value::Int8(_)
212 | Value::Int16(_)
213 | Value::Int32(_)
214 | Value::Int64(_)
215 | Value::Int128(_)
216 | Value::UInt8(_)
217 | Value::UInt16(_)
218 | Value::UInt32(_)
219 | Value::UInt64(_)
220 | Value::UInt128(_)
221 | Value::IntSize(_)
222 | Value::UIntSize(_)
223 | Value::Float32(_)
224 | Value::Float64(_)
225 | Value::BigInteger(_)
226 | Value::BigDecimal(_)
227 )
228}
229
230#[inline]
232fn number_value(value: &Value) -> Option<NumberValue> {
233 match value {
234 Value::Int8(v) => Some(NumberValue::Signed(i128::from(*v))),
235 Value::Int16(v) => Some(NumberValue::Signed(i128::from(*v))),
236 Value::Int32(v) => Some(NumberValue::Signed(i128::from(*v))),
237 Value::Int64(v) => Some(NumberValue::Signed(i128::from(*v))),
238 Value::Int128(v) => Some(NumberValue::Signed(*v)),
239 Value::UInt8(v) => Some(NumberValue::Unsigned(u128::from(*v))),
240 Value::UInt16(v) => Some(NumberValue::Unsigned(u128::from(*v))),
241 Value::UInt32(v) => Some(NumberValue::Unsigned(u128::from(*v))),
242 Value::UInt64(v) => Some(NumberValue::Unsigned(u128::from(*v))),
243 Value::UInt128(v) => Some(NumberValue::Unsigned(*v)),
244 Value::IntSize(v) => Some(NumberValue::Signed(*v as i128)),
245 Value::UIntSize(v) => Some(NumberValue::Unsigned(*v as u128)),
246 Value::Float32(v) => Some(NumberValue::Float(f64::from(*v))),
247 Value::Float64(v) => Some(NumberValue::Float(*v)),
248 _ => None,
249 }
250}
251
252#[inline]
254fn compare_numbers(
255 a: &Value,
256 b: &Value,
257 number_comparison_policy: NumberComparisonPolicy,
258) -> Option<Ordering> {
259 if contains_big_number(a, b) {
260 return compare_big_numbers(a, b, number_comparison_policy);
261 }
262 match (number_value(a)?, number_value(b)?) {
263 (NumberValue::Signed(x), NumberValue::Signed(y)) => Some(x.cmp(&y)),
264 (NumberValue::Unsigned(x), NumberValue::Unsigned(y)) => Some(x.cmp(&y)),
265 (NumberValue::Signed(x), NumberValue::Unsigned(y)) => Some(compare_i128_u128(x, y)),
266 (NumberValue::Unsigned(x), NumberValue::Signed(y)) => {
267 Some(compare_i128_u128(y, x).reverse())
268 }
269 (NumberValue::Signed(x), NumberValue::Float(y)) => {
270 compare_i128_f64(x, y, number_comparison_policy)
271 }
272 (NumberValue::Float(x), NumberValue::Signed(y)) => {
273 compare_i128_f64(y, x, number_comparison_policy).map(Ordering::reverse)
274 }
275 (NumberValue::Unsigned(x), NumberValue::Float(y)) => {
276 compare_u128_f64(x, y, number_comparison_policy)
277 }
278 (NumberValue::Float(x), NumberValue::Unsigned(y)) => {
279 compare_u128_f64(y, x, number_comparison_policy).map(Ordering::reverse)
280 }
281 (NumberValue::Float(x), NumberValue::Float(y)) => x.partial_cmp(&y),
282 }
283}
284
285#[inline]
287fn contains_big_number(a: &Value, b: &Value) -> bool {
288 matches!(a, Value::BigInteger(_) | Value::BigDecimal(_))
289 || matches!(b, Value::BigInteger(_) | Value::BigDecimal(_))
290}
291
292fn compare_big_numbers(
294 a: &Value,
295 b: &Value,
296 number_comparison_policy: NumberComparisonPolicy,
297) -> Option<Ordering> {
298 if let (Some(x), Some(y)) = (big_integer_value(a), big_integer_value(b)) {
299 return Some(x.cmp(&y));
300 }
301 if let (Some(x), Some(y)) = (big_decimal_value(a), big_decimal_value(b)) {
302 return Some(x.cmp(&y));
303 }
304 if matches!(
305 number_comparison_policy,
306 NumberComparisonPolicy::Approximate
307 ) {
308 return compare_as_f64(a, b);
309 }
310 None
311}
312
313fn big_integer_value(value: &Value) -> Option<BigInt> {
315 match value {
316 Value::Int8(v) => Some(BigInt::from(*v)),
317 Value::Int16(v) => Some(BigInt::from(*v)),
318 Value::Int32(v) => Some(BigInt::from(*v)),
319 Value::Int64(v) => Some(BigInt::from(*v)),
320 Value::Int128(v) => Some(BigInt::from(*v)),
321 Value::UInt8(v) => Some(BigInt::from(*v)),
322 Value::UInt16(v) => Some(BigInt::from(*v)),
323 Value::UInt32(v) => Some(BigInt::from(*v)),
324 Value::UInt64(v) => Some(BigInt::from(*v)),
325 Value::UInt128(v) => Some(BigInt::from(*v)),
326 Value::IntSize(v) => Some(BigInt::from(*v)),
327 Value::UIntSize(v) => Some(BigInt::from(*v)),
328 Value::BigInteger(v) => Some(v.clone()),
329 _ => None,
330 }
331}
332
333fn big_decimal_value(value: &Value) -> Option<BigDecimal> {
335 match value {
336 Value::BigDecimal(v) => Some(v.clone()),
337 _ => big_integer_value(value).map(BigDecimal::from),
338 }
339}
340
341#[inline]
343fn compare_as_f64(a: &Value, b: &Value) -> Option<Ordering> {
344 a.to::<f64>().ok()?.partial_cmp(&b.to::<f64>().ok()?)
345}
346
347const MAX_SAFE_INTEGER_F64_U64: u64 = 9_007_199_254_740_992;
348const I64_MIN_F64: f64 = -9_223_372_036_854_775_808.0;
349const I64_EXCLUSIVE_MAX_F64: f64 = 9_223_372_036_854_775_808.0;
350const U64_EXCLUSIVE_MAX_F64: f64 = 18_446_744_073_709_551_616.0;
351
352#[inline]
354fn compare_i128_u128(x: i128, y: u128) -> Ordering {
355 if x < 0 {
356 Ordering::Less
357 } else {
358 (x as u128).cmp(&y)
359 }
360}
361
362fn compare_i128_f64(
364 x: i128,
365 y: f64,
366 number_comparison_policy: NumberComparisonPolicy,
367) -> Option<Ordering> {
368 if let Ok(x64) = i64::try_from(x) {
369 return compare_i64_f64(x64, y, number_comparison_policy);
370 }
371 if matches!(
372 number_comparison_policy,
373 NumberComparisonPolicy::Approximate
374 ) {
375 return (x as f64).partial_cmp(&y);
376 }
377 None
378}
379
380fn compare_u128_f64(
382 x: u128,
383 y: f64,
384 number_comparison_policy: NumberComparisonPolicy,
385) -> Option<Ordering> {
386 if let Ok(x64) = u64::try_from(x) {
387 return compare_u64_f64(x64, y, number_comparison_policy);
388 }
389 if matches!(
390 number_comparison_policy,
391 NumberComparisonPolicy::Approximate
392 ) {
393 return (x as f64).partial_cmp(&y);
394 }
395 None
396}
397
398fn compare_i64_f64(
400 x: i64,
401 y: f64,
402 number_comparison_policy: NumberComparisonPolicy,
403) -> Option<Ordering> {
404 if y.fract() == 0.0 && (I64_MIN_F64..I64_EXCLUSIVE_MAX_F64).contains(&y) {
405 return Some(x.cmp(&(y as i64)));
406 }
407
408 if x.unsigned_abs() <= MAX_SAFE_INTEGER_F64_U64 {
409 return (x as f64).partial_cmp(&y);
410 }
411
412 if matches!(
413 number_comparison_policy,
414 NumberComparisonPolicy::Approximate
415 ) {
416 return (x as f64).partial_cmp(&y);
417 }
418
419 None
420}
421
422fn compare_u64_f64(
424 x: u64,
425 y: f64,
426 number_comparison_policy: NumberComparisonPolicy,
427) -> Option<Ordering> {
428 if y < 0.0 {
429 return Some(Ordering::Greater);
430 }
431
432 if y.fract() == 0.0 && (0.0..U64_EXCLUSIVE_MAX_F64).contains(&y) {
433 return Some(x.cmp(&(y as u64)));
434 }
435
436 if x <= MAX_SAFE_INTEGER_F64_U64 {
437 return (x as f64).partial_cmp(&y);
438 }
439
440 if matches!(
441 number_comparison_policy,
442 NumberComparisonPolicy::Approximate
443 ) {
444 return (x as f64).partial_cmp(&y);
445 }
446
447 None
448}