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