uqa_sql/expr/
temporal_input.rs1use std::{cell::Cell, marker::PhantomData};
10use uqa_core::TemporalDateOrder;
11
12thread_local! {
13 static DATE_ORDER: Cell<TemporalDateOrder> = const { Cell::new(TemporalDateOrder::MonthDayYear) };
14}
15
16#[must_use]
17pub fn temporal_date_order() -> TemporalDateOrder {
18 DATE_ORDER.with(Cell::get)
19}
20
21pub struct DateOrderScope {
23 previous: TemporalDateOrder,
24 thread: PhantomData<std::rc::Rc<()>>,
25}
26
27impl DateOrderScope {
28 #[must_use]
29 pub fn enter(order: TemporalDateOrder) -> Self {
30 Self {
31 previous: DATE_ORDER.with(|current| current.replace(order)),
32 thread: PhantomData,
33 }
34 }
35}
36
37impl Drop for DateOrderScope {
38 fn drop(&mut self) {
39 DATE_ORDER.with(|current| current.set(self.previous));
40 }
41}
42
43#[derive(Clone, Copy)]
45pub struct TemporalInputContext {
46 pub transaction_clock_micros: Option<i64>,
47 pub date_order: TemporalDateOrder,
48}
49
50impl TemporalInputContext {
51 #[must_use]
52 pub fn current() -> Self {
53 Self {
54 transaction_clock_micros: super::transaction_clock_micros(),
55 date_order: temporal_date_order(),
56 }
57 }
58
59 #[must_use]
60 pub fn enter(self) -> TemporalInputScope {
61 TemporalInputScope {
62 _clock: self
63 .transaction_clock_micros
64 .map(super::TransactionClockScope::enter),
65 _order: DateOrderScope::enter(self.date_order),
66 }
67 }
68}
69
70pub struct TemporalInputScope {
71 _clock: Option<super::TransactionClockScope>,
72 _order: DateOrderScope,
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use uqa_core::Value;
79
80 #[test]
81 fn nested_input_contexts_restore_clocks_and_orders_after_failure() {
82 let _caller = TemporalInputContext {
83 transaction_clock_micros: Some(10),
84 date_order: TemporalDateOrder::DayMonthYear,
85 }
86 .enter();
87 {
88 let _nested = TemporalInputContext {
89 transaction_clock_micros: Some(20),
90 date_order: TemporalDateOrder::YearMonthDay,
91 }
92 .enter();
93 assert_eq!(super::super::transaction_clock_micros(), Some(20));
94 assert!(crate::expr::cast_value(&Value::Str("13/02/2020".into()), "date").is_err());
95 }
96 assert_eq!(super::super::transaction_clock_micros(), Some(10));
97 assert_eq!(temporal_date_order(), TemporalDateOrder::DayMonthYear);
98 let Value::Temporal(value) =
99 crate::expr::cast_value(&Value::Str("02/03/2020".into()), "date").unwrap()
100 else {
101 panic!("date")
102 };
103 assert_eq!(value.to_sql_string(), "2020-03-02");
104 }
105}