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