1#![warn(missing_docs)]
4
5use crate::{
13 DataType, MyError,
14 bound::Bound,
15 geom::{G, GTrait},
16 qstring::QString,
17};
18use core::fmt;
19use geos::{Geom, Geometry};
20use jiff::{Timestamp, Zoned, civil::Date, tz::TimeZone};
21use std::{cmp::Ordering, mem};
22use tracing::error;
23
24#[derive(Clone)]
26pub enum Q {
27 Null,
29 Bool(bool),
31 Num(f64),
33 Str(QString),
36 Geom(Geometry),
38 Instant(Bound),
40 Interval(Bound, Bound),
42 List(Vec<Q>),
44}
45
46impl fmt::Debug for Q {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 match self {
49 Self::Null => write!(f, "Null"),
50 Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
51 Self::Num(arg0) => f.debug_tuple("Num").field(arg0).finish(),
52 Self::Str(arg0) => f.debug_tuple("Str").field(arg0).finish(),
53 Self::Geom(x) => write!(f, "Geom({})", x.to_wkt().expect("Failed generating WKT")),
54 Self::Instant(arg0) => f.debug_tuple("Instant").field(arg0).finish(),
55 Self::Interval(arg0, arg1) => {
56 f.debug_tuple("Interval").field(arg0).field(arg1).finish()
57 }
58 Self::List(arg0) => f.debug_tuple("List").field(arg0).finish(),
59 }
60 }
61}
62
63impl PartialEq for Q {
64 fn eq(&self, other: &Self) -> bool {
65 match (self, other) {
66 (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
67 (Self::Num(l0), Self::Num(r0)) => l0 == r0,
68 (Self::Str(l0), Self::Str(r0)) => l0 == r0,
69 (Self::Geom(l0), Self::Geom(r0)) => l0 == r0,
70 (Self::Instant(l0), Self::Instant(r0)) => l0 == r0,
71 (Self::Interval(l0, l1), Self::Interval(r0, r1)) => l0 == r0 && l1 == r1,
72 (Self::List(l0), Self::List(r0)) => l0 == r0,
73 _ => mem::discriminant(self) == mem::discriminant(other),
74 }
75 }
76}
77
78impl Eq for Q {}
79
80impl PartialOrd for Q {
81 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
82 match (self, other) {
83 (Q::Null, Q::Null) => Some(Ordering::Equal),
84 (Q::Bool(a), Q::Bool(b)) => a.partial_cmp(b),
85 (Q::Num(a), Q::Num(b)) => a.partial_cmp(b),
86 (Q::Str(a), Q::Str(b)) => a.partial_cmp(b),
87 (Q::Instant(a), Q::Instant(b)) => a.partial_cmp(b),
88 (Q::Interval(a0, a1), Q::Interval(b0, b1)) => match a0.partial_cmp(b0) {
89 Some(Ordering::Equal) => match (a1, b1) {
90 (Bound::None, Bound::None) => Some(Ordering::Equal),
91 (Bound::None, _) => Some(Ordering::Greater),
92 (_, Bound::None) => Some(Ordering::Less),
93 _ => a1.partial_cmp(b1),
94 },
95 x => x,
96 },
97 (Q::List(a), Q::List(b)) => a.partial_cmp(b),
98 _ => None,
100 }
101 }
102}
103
104impl fmt::Display for Q {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 match self {
107 Q::Null => write!(f, "Null"),
108 Q::Bool(x) => write!(f, "{x}"),
109 Q::Num(x) => write!(f, "{x}"),
110 Q::Str(x) => write!(f, "{x}"),
111 Q::Geom(x) => write!(f, "{}", x.to_wkt().expect("Failed generating WKT")),
112 Q::Instant(x) => write!(f, "{x}"),
113 Q::Interval(Bound::None, Bound::None) => write!(f, "[...]"),
114 Q::Interval(Bound::None, y) => write!(f, "[..{y}]"),
115 Q::Interval(x, Bound::None) => write!(f, "[{x}..]"),
116 Q::Interval(x, y) => write!(f, "[{x}..{y}]"),
117 Q::List(x) => write!(f, "{x:?}"),
118 }
119 }
120}
121
122impl Q {
123 pub fn new_plain_str(value: &str) -> Self {
126 Self::Str(QString::plain(value.trim()))
127 }
128
129 pub fn try_from_timestamp_str(value: &str) -> Result<Self, MyError> {
132 let x = value.parse::<Timestamp>()?;
133 let z = x.to_zoned(TimeZone::UTC);
134 Ok(Q::Instant(Bound::Timestamp(z)))
135 }
136
137 pub fn try_from_timestamp_ns(value: i128) -> Result<Self, MyError> {
140 let x = Timestamp::from_nanosecond(value)?;
141 let z = x.to_zoned(TimeZone::UTC);
142 Ok(Q::Instant(Bound::Timestamp(z)))
143 }
144
145 pub fn try_from_date_str(value: &str) -> Result<Self, MyError> {
152 let x = value.parse::<Date>()?;
153 let z = x.to_zoned(TimeZone::UTC)?;
154 Ok(Q::Instant(Bound::Date(z)))
155 }
156
157 pub fn try_from_date_ns(value: i128) -> Result<Self, MyError> {
160 let x = Timestamp::from_nanosecond(value)?;
161 let z = x.to_zoned(TimeZone::UTC);
162 Ok(Q::Instant(Bound::Date(z)))
163 }
164
165 pub fn try_from_wkt(value: &str) -> Result<Self, MyError> {
167 let g = G::try_from_wkt(value)?;
168 let g_geos = g.to_geos()?;
169 Ok(Q::from(g_geos))
170 }
171
172 pub(crate) fn is_null(&self) -> bool {
174 matches!(self, Q::Null)
175 }
176
177 pub(crate) fn is_instant(&self) -> bool {
179 matches!(self, Q::Instant(_))
180 }
181
182 pub(crate) fn to_bool(&self) -> Result<bool, MyError> {
183 match self {
184 Q::Bool(x) => Ok(*x),
185 _ => Err(MyError::Runtime(format!("{self} is not a boolean").into())),
186 }
187 }
188
189 pub(crate) fn to_str(&self) -> Result<QString, MyError> {
190 match self {
191 Q::Str(x) => Ok(x.to_owned()),
192 _ => Err(MyError::Runtime(format!("{self} is not a string").into())),
193 }
194 }
195
196 pub(crate) fn to_num(&self) -> Result<f64, MyError> {
197 match self {
198 Q::Num(x) => Ok(*x),
199 _ => Err(MyError::Runtime(format!("{self} is not a number").into())),
200 }
201 }
202
203 pub(crate) fn to_geom(&self) -> Result<Geometry, MyError> {
204 let mut g = match self {
205 Q::Geom(x) => x.to_owned(),
206 _ => return Err(MyError::Runtime(format!("{self} is not a geometry").into())),
207 };
208 match g.normalize() {
209 Ok(_) => Ok(g),
210 Err(x) => Err(MyError::Runtime(format!("Failed normalizing: {x}").into())),
211 }
212 }
213
214 pub(crate) fn to_bound(&self) -> Result<Bound, MyError> {
215 match self {
216 Q::Instant(x) => Ok(x.to_owned()),
217 _ => Err(MyError::Runtime(
218 format!("{self} is not a bounded instant").into(),
219 )),
220 }
221 }
222
223 pub(crate) fn to_interval(&self) -> Result<(Bound, Bound), MyError> {
224 match self {
225 Q::Interval(x, y) => Ok((x.to_owned(), y.to_owned())),
226 _ => Err(MyError::Runtime(
227 format!("{self} is not an interval").into(),
228 )),
229 }
230 }
231
232 pub(crate) fn to_list(&self) -> Result<Vec<Q>, MyError> {
233 match self {
234 Q::List(x) => Ok(x.to_owned()),
235 _ => Err(MyError::Runtime(format!("{self} is not a list").into())),
236 }
237 }
238
239 pub(crate) fn same_type(this: &Self, that: &Self) -> bool {
241 mem::discriminant(this) == mem::discriminant(that)
242 }
243
244 pub(crate) fn literal_type(&self) -> Option<DataType> {
246 match self {
247 Q::Bool(_) => Some(DataType::Bool),
248 Q::Num(_) => Some(DataType::Num),
249 Q::Str(_) => Some(DataType::Str),
250 Q::Geom(_) => Some(DataType::Geom),
251 Q::Instant(x) => match x {
252 Bound::None => None,
253 Bound::Date(_) => Some(DataType::Date),
254 Bound::Timestamp(_) => Some(DataType::Timestamp),
255 },
256 _ => None,
258 }
259 }
260
261 pub(crate) fn contained_by(&self, list: Vec<Self>) -> Result<bool, MyError> {
263 if list.is_empty() {
264 return Ok(false);
265 }
266
267 if let Some(z_type) = self.literal_type() {
268 if matches!(z_type, DataType::Bool) {
269 let lhs = self.to_bool()?;
270 let rhs: Result<Vec<bool>, MyError> = list.iter().map(|e| e.to_bool()).collect();
271 let rhs = rhs?;
272 Ok(rhs.contains(&lhs))
273 } else if matches!(z_type, DataType::Num) {
274 let lhs = self.to_num()?;
275 let rhs: Result<Vec<f64>, MyError> = list.iter().map(|e| e.to_num()).collect();
276 let rhs = rhs?;
277 Ok(rhs.contains(&lhs))
278 } else if matches!(z_type, DataType::Str) {
279 let lhs = &self.to_str()?;
280 let rhs: Result<Vec<QString>, MyError> = list.iter().map(|e| e.to_str()).collect();
281 let rhs = rhs?;
282 Ok(rhs.contains(lhs))
283 } else if matches!(z_type, DataType::Date) || matches!(z_type, DataType::Timestamp) {
284 let lhs = self.to_bound()?.as_zoned().unwrap();
285 let rhs: Result<Vec<Zoned>, MyError> = list
286 .iter()
287 .map(|e| e.to_bound().and_then(|b| b.to_zoned()))
288 .collect();
289 let rhs = rhs?;
290 Ok(rhs.contains(&lhs))
291 } else if matches!(z_type, DataType::Geom) {
292 let lhs = self.to_geom()?;
293 let rhs: Result<Vec<Geometry>, MyError> =
294 list.iter().map(|e| e.to_geom()).collect();
295 let rhs = rhs?;
296 Ok(rhs.contains(&lhs))
297 } else {
298 error!("Failed. self = {self:?}; list = {list:?}");
299 Ok(false)
300 }
301 } else {
302 Ok(false)
303 }
304 }
305}
306
307impl From<bool> for Q {
308 fn from(value: bool) -> Self {
309 Q::Bool(value)
310 }
311}
312
313trait TryToF64<T> {
316 fn try_to_f64(self) -> Result<f64, MyError>;
317}
318
319macro_rules! impl_safe_try_to_f64 {
321 ($($t:ty),*) => {
322 $(
323 impl TryToF64<$t> for $t {
324 fn try_to_f64(self) -> Result<f64, $crate::MyError> {
325 Ok(self as f64)
326 }
327 }
328 )*
329 };
330}
331
332impl_safe_try_to_f64!(u8, u16, u32, i8, i16, i32);
334
335macro_rules! impl_try_unsigned_to_f64 {
343 ($($t:ty),*) => {
344 $(
345 impl TryToF64<$t> for $t {
346 fn try_to_f64(self) -> Result<f64, $crate::MyError> {
347 const MAX_LIMIT: $t = (1 << 53) - 1;
348
349 if self <= MAX_LIMIT {
350 Ok(self as f64)
351 } else {
352 Err(MyError::PrecisionLoss(self.to_string().into()))
353 }
354 }
355 }
356 )*
357 };
358}
359
360impl_try_unsigned_to_f64!(u64, u128);
361
362macro_rules! impl_try_signed_to_f64 {
364 ($($t:ty),*) => {
365 $(
366 impl TryToF64<$t> for $t {
367 fn try_to_f64(self) -> Result<f64, $crate::MyError> {
368 const MAX_LIMIT: $t = (1 << 53) - 1;
369 const MIN_LIMIT: $t = - MAX_LIMIT;
371
372 if (MIN_LIMIT..=MAX_LIMIT).contains(&self) {
373 Ok(self as f64)
374 } else {
375 Err(MyError::PrecisionLoss(self.to_string().into()))
376 }
377 }
378 }
379 )*
380 };
381}
382
383impl_try_signed_to_f64!(i64, i128);
384
385impl TryToF64<usize> for usize {
389 fn try_to_f64(self) -> Result<f64, MyError> {
390 match usize::BITS {
391 32 => (self as u32).try_to_f64(),
392 _ => (self as u64).try_to_f64(),
393 }
394 }
395}
396
397impl TryToF64<isize> for isize {
398 fn try_to_f64(self) -> Result<f64, MyError> {
399 match isize::BITS {
400 32 => (self as i32).try_to_f64(),
401 _ => (self as i64).try_to_f64(),
402 }
403 }
404}
405
406macro_rules! impl_try_from_int {
408 ($($t:ty),*) => {
409 $(
410 impl TryFrom<$t> for $crate::Q {
411 type Error = MyError;
412 fn try_from(value: $t) -> Result<Self, $crate::MyError> {
413 let x = value.try_to_f64()?;
414 Ok(Q::Num(x))
415 }
416 }
417 )*
418 };
419}
420
421impl_try_from_int!(
422 u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
423);
424
425impl From<f64> for Q {
426 fn from(value: f64) -> Self {
427 Q::Num(value)
428 }
429}
430
431impl From<Geometry> for Q {
432 fn from(value: Geometry) -> Self {
433 Q::Geom(value)
434 }
435}
436
437impl TryFrom<Date> for Q {
438 type Error = MyError;
439
440 fn try_from(value: Date) -> Result<Self, Self::Error> {
441 let z = value.to_zoned(TimeZone::UTC)?;
442 Ok(Q::Instant(Bound::Date(z)))
443 }
444}
445
446impl From<Timestamp> for Q {
447 fn from(value: Timestamp) -> Self {
448 let z = value.to_zoned(TimeZone::UTC);
449 Q::Instant(Bound::Timestamp(z))
450 }
451}
452
453impl From<Bound> for Q {
454 fn from(value: Bound) -> Self {
455 Q::Instant(value)
456 }
457}
458
459#[cfg(test)]
460mod tests {
461 use super::*;
462 use rand::Rng;
463
464 #[test]
465 fn test_usize_max() {
466 let x = usize::MAX.try_to_f64();
467 assert!(matches!(x.err(), Some(MyError::PrecisionLoss(_))))
468 }
469
470 #[test]
471 fn test_usize() {
472 let x: usize = (1 << 53) - 1;
473 let y1 = x as f64;
474 let y2 = x.try_to_f64().expect("Failed");
475 assert_eq!(y1, y2)
476 }
477
478 #[test]
479 fn test_u128_max() {
480 let x = u128::MAX.try_to_f64();
481 assert!(matches!(x.err(), Some(MyError::PrecisionLoss(_))))
482 }
483
484 #[test]
485 fn test_u128() {
486 let x: u128 = (1 << 53) - 1;
487 let y1 = x as f64;
488 let y2 = x.try_to_f64().expect("Failed");
489 assert_eq!(y1, y2)
490 }
491
492 #[test]
493 fn test_u64_max() {
494 let x = u64::MAX.try_to_f64();
495 assert!(matches!(x.err(), Some(MyError::PrecisionLoss(_))))
496 }
497
498 #[test]
499 fn test_u64() {
500 let x: u64 = (1 << 53) - 1;
501 let y1 = x as f64;
502 let y2 = x.try_to_f64().expect("Failed");
503 assert_eq!(y1, y2)
504 }
505
506 #[test]
507 fn test_isize_max() {
508 let x = isize::MAX.try_to_f64();
509 assert!(matches!(x.err(), Some(MyError::PrecisionLoss(_))))
510 }
511
512 #[test]
513 fn test_isize() {
514 let x1: isize = (1 << 53) - 1;
515 let y1 = x1 as f64;
516 let y2 = x1.try_to_f64().expect("Failed");
517 assert_eq!(y1, y2);
518
519 let x2 = -x1;
520 let y1 = x2 as f64;
521 let y2 = x2.try_to_f64().expect("Failed");
522 assert_eq!(y1, y2)
523 }
524
525 #[test]
526 fn test_isize_min() {
527 let x = isize::MIN.try_to_f64();
528 assert!(matches!(x.err(), Some(MyError::PrecisionLoss(_))))
529 }
530
531 #[test]
532 fn test_i128_max() {
533 let x = i128::MAX.try_to_f64();
534 assert!(matches!(x.err(), Some(MyError::PrecisionLoss(_))))
535 }
536
537 #[test]
538 fn test_i128() {
539 let x1: i128 = (1 << 53) - 1;
540 let y1 = x1 as f64;
541 let y2 = x1.try_to_f64().expect("Failed");
542 assert_eq!(y1, y2);
543
544 let x2 = -x1;
545 let y1 = x2 as f64;
546 let y2 = x2.try_to_f64().expect("Failed");
547 assert_eq!(y1, y2)
548 }
549
550 #[test]
551 fn test_i128_min() {
552 let x = i128::MIN.try_to_f64();
553 assert!(matches!(x.err(), Some(MyError::PrecisionLoss(_))))
554 }
555
556 #[test]
557 fn test_i64_max() {
558 let x = i64::MAX.try_to_f64();
559 assert!(matches!(x.err(), Some(MyError::PrecisionLoss(_))))
560 }
561
562 #[test]
563 fn test_i64_min() {
564 let x = i64::MIN.try_to_f64();
565 assert!(matches!(x.err(), Some(MyError::PrecisionLoss(_))))
566 }
567
568 #[test]
569 fn test_i64() {
570 let x1: i64 = (1 << 53) - 1;
571 let y1 = x1 as f64;
572 let y2 = x1.try_to_f64().expect("Failed");
573 assert_eq!(y1, y2);
574
575 let x2 = -x1;
576 let y1 = x2 as f64;
577 let y2 = x2.try_to_f64().expect("Failed");
578 assert_eq!(y1, y2)
579 }
580
581 #[test]
582 fn fuzz_test_i64() {
584 const LIMIT: i64 = (1 << 53) - 1;
585
586 fn random_i64() -> i64 {
587 let mut rng = rand::rng();
588 match rng.random_bool(0.5) {
589 true => LIMIT - rng.random_range(1..=LIMIT.abs()),
590 false => LIMIT + rng.random_range(1..=LIMIT.abs()),
591 }
592 }
593
594 let mut expected = 0;
595 let mut actual = 0;
596 for _ in 0..1000 {
597 let x = random_i64();
598 if !(-LIMIT..=LIMIT).contains(&x) {
599 expected += 1;
600 }
601 match Q::try_from(x) {
603 Ok(_) => (), Err(MyError::PrecisionLoss(_)) => actual += 1,
605 Err(x) => panic!("Unexpected {x}"),
606 }
607 }
608
609 assert_eq!(expected, actual)
611 }
612
613 #[test]
614 fn fuzz_test_u64() {
616 const LIMIT: u64 = (1 << 53) - 1;
617
618 fn random_u64() -> u64 {
619 let mut rng = rand::rng();
620 match rng.random_bool(0.5) {
621 true => LIMIT.saturating_sub(rng.random_range(1..=LIMIT)),
622 false => LIMIT + rng.random_range(1..=LIMIT),
623 }
624 }
625
626 let mut expected = 0;
627 let mut actual = 0;
628 for _ in 0..1000 {
629 let x = random_u64();
630 if x > LIMIT {
631 expected += 1;
632 }
633 match Q::try_from(x) {
635 Ok(_) => (), Err(MyError::PrecisionLoss(_)) => actual += 1,
637 Err(x) => panic!("Unexpected {x}"),
638 }
639 }
640
641 assert_eq!(expected, actual)
643 }
644
645 #[test]
646 fn test_like() {
648 let input = QString::plain("hello");
650 let pattern = QString::plain("h%o");
651 let r1 = QString::like(&input, &pattern);
652 assert!(r1);
653
654 let input = QString::icase("HELLO");
656 let pattern = QString::plain("h%o");
657 let r2 = QString::like(&input, &pattern);
658 assert!(r2);
659 let input = QString::icase("HELLODOLLY");
660 let pattern = QString::plain("h%odo%y");
661 let r2p = QString::like(&input, &pattern);
662 assert!(r2p);
663
664 let input = QString::plain("hello");
666 let pattern = QString::icase("h__lo");
667 let r3 = QString::like(&input, &pattern);
668 assert!(r3);
669 let pattern = QString::icase("h%lo");
671 let r3p = QString::like(&input, &pattern);
672 assert!(r3p);
673
674 let input = QString::plain("hello");
676 let pattern = QString::plain("h\\%o");
677 let r4 = QString::like(&input, &pattern);
678 assert!(!r4);
679
680 let input = QString::plain("h%llo");
681 let pattern = QString::plain("h\\%llo");
682 let r5 = QString::like(&input, &pattern);
683 assert!(r5);
684
685 let input = QString::plain("");
687 let pattern = QString::plain("%");
688 let r6 = QString::like(&input, &pattern);
689 assert!(r6);
690
691 let input = QString::plain("abc");
693 let pattern = QString::plain("");
694 let r7 = QString::like(&input, &pattern);
695 assert!(!r7);
696
697 let input = QString::icase("ß"); let pattern = QString::icase("ẞ"); let u1 = QString::like(&input, &pattern);
702 assert!(u1);
703
704 let input = QString::icase("Σ");
705 let pattern = QString::plain("σ");
706 let u2 = QString::like(&input, &pattern);
707 assert!(u2);
708
709 let input = QString::plain("こんにちは");
718 let pattern = QString::plain("こ%は");
719 let u4 = QString::like(&input, &pattern);
720 assert!(u4);
721
722 let pattern = QString::icase("こ_にちは");
723 let u5 = QString::like(&input, &pattern);
724 assert!(u5);
725 }
726}