Skip to main content

lutra_bin/
std_types.rs

1use crate::{Decode, Encode, Layout, Result};
2
3/// Standard-library types used by generated Lutra code.
4pub mod ops {
5    /// Result of a three-way comparison.
6    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7    #[allow(non_camel_case_types)]
8    pub enum Ordering {
9        Less,
10        Equal,
11        Greater,
12    }
13}
14
15/// An instant in time — microseconds since 1970-01-01T00:00:00 UTC.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct Timestamp {
18    pub microseconds: i64,
19}
20
21/// A calendar date — days since 1970-01-01.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct Date {
24    pub days_epoch: i32,
25}
26
27/// A time-of-day offset — microseconds since midnight.
28#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct Time {
30    pub microseconds: i64,
31}
32
33/// A fixed-point decimal (scale = 2).
34#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub struct Decimal(pub i64);
36
37impl Encode for Timestamp {
38    type HeadPtr = ();
39
40    fn encode_head(&self, buf: &mut crate::bytes::BytesMut) {
41        self.microseconds.encode_head(buf)
42    }
43
44    fn encode_body(&self, _: (), _: &mut crate::bytes::BytesMut) {}
45}
46
47impl Layout for Timestamp {
48    fn head_size() -> usize {
49        i64::head_size()
50    }
51}
52
53impl Decode for Timestamp {
54    fn decode(buf: &[u8]) -> Result<Self> {
55        Ok(Self {
56            microseconds: i64::decode(buf)?,
57        })
58    }
59}
60impl Encode for Date {
61    type HeadPtr = ();
62
63    fn encode_head(&self, buf: &mut crate::bytes::BytesMut) {
64        self.days_epoch.encode_head(buf)
65    }
66
67    fn encode_body(&self, _: (), _: &mut crate::bytes::BytesMut) {}
68}
69impl Layout for Date {
70    fn head_size() -> usize {
71        i32::head_size()
72    }
73}
74impl Decode for Date {
75    fn decode(buf: &[u8]) -> Result<Self> {
76        Ok(Self {
77            days_epoch: i32::decode(buf)?,
78        })
79    }
80}
81impl Encode for Time {
82    type HeadPtr = ();
83
84    fn encode_head(&self, buf: &mut crate::bytes::BytesMut) {
85        self.microseconds.encode_head(buf)
86    }
87
88    fn encode_body(&self, _: (), _: &mut crate::bytes::BytesMut) {}
89}
90impl Layout for Time {
91    fn head_size() -> usize {
92        i64::head_size()
93    }
94}
95impl Decode for Time {
96    fn decode(buf: &[u8]) -> Result<Self> {
97        Ok(Self {
98            microseconds: i64::decode(buf)?,
99        })
100    }
101}
102impl Encode for Decimal {
103    type HeadPtr = ();
104
105    fn encode_head(&self, buf: &mut crate::bytes::BytesMut) {
106        self.0.encode_head(buf)
107    }
108
109    fn encode_body(&self, _: (), _: &mut crate::bytes::BytesMut) {}
110}
111impl Layout for Decimal {
112    fn head_size() -> usize {
113        i64::head_size()
114    }
115}
116impl Decode for Decimal {
117    fn decode(buf: &[u8]) -> Result<Self> {
118        Ok(Self(i64::decode(buf)?))
119    }
120}
121impl Encode for ops::Ordering {
122    type HeadPtr = ();
123
124    fn encode_head(&self, buf: &mut crate::bytes::BytesMut) {
125        let tag = match self {
126            Self::Less => 0_u8,
127            Self::Equal => 1,
128            Self::Greater => 2,
129        };
130        tag.encode_head(buf)
131    }
132
133    fn encode_body(&self, _: (), _: &mut crate::bytes::BytesMut) {}
134}
135impl Layout for ops::Ordering {
136    fn head_size() -> usize {
137        8
138    }
139}
140impl Decode for ops::Ordering {
141    fn decode(buf: &[u8]) -> Result<Self> {
142        Ok(match u8::decode(buf)? {
143            0 => Self::Less,
144            1 => Self::Equal,
145            2 => Self::Greater,
146            _ => return Err(crate::Error::InvalidData),
147        })
148    }
149}
150
151#[cfg(feature = "chrono")]
152mod chrono_impls {
153    use chrono::{Datelike, Timelike};
154
155    use super::{Date, Time, Timestamp};
156
157    const EPOCH_DAYS_FROM_CE: i32 = 719_163;
158    const MICROS_PER_SECOND: u32 = 1_000_000;
159    const MICROS_PER_DAY: i64 = 86_400_000_000;
160
161    impl TryFrom<Timestamp> for chrono::DateTime<chrono::Utc> {
162        type Error = crate::Error;
163
164        fn try_from(value: Timestamp) -> core::result::Result<Self, Self::Error> {
165            chrono::DateTime::from_timestamp_micros(value.microseconds)
166                .ok_or(crate::Error::InvalidData)
167        }
168    }
169
170    impl From<chrono::DateTime<chrono::Utc>> for Timestamp {
171        fn from(value: chrono::DateTime<chrono::Utc>) -> Self {
172            Self {
173                microseconds: value.timestamp_micros(),
174            }
175        }
176    }
177
178    impl TryFrom<Date> for chrono::NaiveDate {
179        type Error = crate::Error;
180
181        fn try_from(value: Date) -> core::result::Result<Self, Self::Error> {
182            chrono::NaiveDate::from_num_days_from_ce_opt(value.days_epoch + EPOCH_DAYS_FROM_CE)
183                .ok_or(crate::Error::InvalidData)
184        }
185    }
186
187    impl TryFrom<chrono::NaiveDate> for Date {
188        type Error = crate::Error;
189
190        fn try_from(value: chrono::NaiveDate) -> core::result::Result<Self, Self::Error> {
191            let days = value.num_days_from_ce() - EPOCH_DAYS_FROM_CE;
192            Ok(Self { days_epoch: days })
193        }
194    }
195
196    impl TryFrom<Time> for chrono::NaiveTime {
197        type Error = crate::Error;
198
199        fn try_from(value: Time) -> core::result::Result<Self, Self::Error> {
200            if !(0..MICROS_PER_DAY).contains(&value.microseconds) {
201                return Err(crate::Error::InvalidData);
202            }
203
204            let secs = u32::try_from(value.microseconds / i64::from(MICROS_PER_SECOND))
205                .map_err(|_| crate::Error::InvalidData)?;
206            let micros = u32::try_from(value.microseconds % i64::from(MICROS_PER_SECOND))
207                .map_err(|_| crate::Error::InvalidData)?;
208
209            chrono::NaiveTime::from_num_seconds_from_midnight_opt(secs, micros * 1_000)
210                .ok_or(crate::Error::InvalidData)
211        }
212    }
213
214    impl From<chrono::NaiveTime> for Time {
215        fn from(value: chrono::NaiveTime) -> Self {
216            let secs = i64::from(value.num_seconds_from_midnight());
217            let micros = i64::from(value.nanosecond() / 1_000);
218            Self {
219                microseconds: secs * i64::from(MICROS_PER_SECOND) + micros,
220            }
221        }
222    }
223}