1use core::marker::PhantomData;
2use core::ops::{Add, Mul};
3use generic_array::{typenum as t, ArrayLength, GenericArray};
4
5mod sealed {
6 pub trait Sealed {}
7}
8
9type I<BOOL> = t::UInt<t::UTerm, BOOL>;
11
12type F4<F> = t::Prod<I<F>, t::U4>;
14type O5<O> = t::Prod<I<O>, t::U5>;
16type P17<P> = t::Sum<P, t::U17>;
18type P18<P> = t::Sum<P17<P>, I<t::Gr<P, t::U0>>>; type F4O5<F, O> = t::Sum<F4<F>, O5<O>>;
20
21type StrLen<F, O, P> = t::Sum<P18<P>, F4O5<F, O>>;
22
23#[doc(hidden)]
24pub struct FormatString<F, O, P>(PhantomData<(F, O, P)>);
25
26impl<F, O, P> sealed::Sealed for FormatString<F, O, P> {}
27
28#[doc(hidden)]
29pub trait IsValidFormat: sealed::Sealed {
30 type Length: ArrayLength;
31 type Storage: AsRef<[u8]> + AsMut<[u8]> + Clone + Copy + Default;
32}
33
34impl<F, O, P> IsValidFormat for FormatString<F, O, P>
35where
36 F: t::Bit,
37 I<F>: Mul<t::U4>,
38 O: t::Bit,
39 I<O>: Mul<t::U5>,
40 P: t::Unsigned + Add<t::U17> + t::IsLessOrEqual<t::U9, Output = t::True> + t::IsGreater<t::U0>,
41 F4<F>: Add<O5<O>>,
42 P17<P>: Add<I<t::Gr<P, t::U0>>>,
43 P18<P>: Add<F4O5<F, O>>,
44 StrLen<F, O, P>: ArrayLength,
45
46 <StrLen<F, O, P> as ArrayLength>::ArrayType<u8>: Copy,
47{
48 type Length = StrLen<F, O, P>;
49 type Storage = GenericArray<u8, Self::Length>;
50}
51
52#[allow(unused_assignments)]
53#[inline(always)]
54#[rustfmt::skip]
55pub fn template<F: t::Bit, O: t::Bit, P: t::Unsigned>() -> <FormatString<F, O, P> as IsValidFormat>::Storage
56where
57 FormatString<F, O, P>: IsValidFormat,
58{
59 let mut value: <FormatString<F, O, P> as IsValidFormat>::Storage = Default::default();
60
61 macro_rules! w {
62 ($x:literal) => {value.as_mut().copy_from_slice($x)};
63 }
64
65 match (F::BOOL, O::BOOL, P::USIZE) {
66 (true, true, 0) => w!(b"+0000-00-00T00:00:00+00:00"),
67 (true, true, 1) => w!(b"+0000-00-00T00:00:00.0+00:00"),
68 (true, true, 2) => w!(b"+0000-00-00T00:00:00.00+00:00"),
69 (true, true, 3) => w!(b"+0000-00-00T00:00:00.000+00:00"),
70 (true, true, 4) => w!(b"+0000-00-00T00:00:00.0000+00:00"),
71 (true, true, 5) => w!(b"+0000-00-00T00:00:00.00000+00:00"),
72 (true, true, 6) => w!(b"+0000-00-00T00:00:00.000000+00:00"),
73 (true, true, 7) => w!(b"+0000-00-00T00:00:00.0000000+00:00"),
74 (true, true, 8) => w!(b"+0000-00-00T00:00:00.00000000+00:00"),
75 (true, true, 9) => w!(b"+0000-00-00T00:00:00.000000000+00:00"),
76 (true, false, 0) => w!(b"+0000-00-00T00:00:00Z"),
77 (true, false, 1) => w!(b"+0000-00-00T00:00:00.0Z"),
78 (true, false, 2) => w!(b"+0000-00-00T00:00:00.00Z"),
79 (true, false, 3) => w!(b"+0000-00-00T00:00:00.000Z"),
80 (true, false, 4) => w!(b"+0000-00-00T00:00:00.0000Z"),
81 (true, false, 5) => w!(b"+0000-00-00T00:00:00.00000Z"),
82 (true, false, 6) => w!(b"+0000-00-00T00:00:00.000000Z"),
83 (true, false, 7) => w!(b"+0000-00-00T00:00:00.0000000Z"),
84 (true, false, 8) => w!(b"+0000-00-00T00:00:00.00000000Z"),
85 (true, false, 9) => w!(b"+0000-00-00T00:00:00.000000000Z"),
86 (false, true, 0) => w!(b"+00000000T000000+00:00"),
87 (false, true, 1) => w!(b"+00000000T000000.0+00:00"),
88 (false, true, 2) => w!(b"+00000000T000000.00+00:00"),
89 (false, true, 3) => w!(b"+00000000T000000.000+00:00"),
90 (false, true, 4) => w!(b"+00000000T000000.0000+00:00"),
91 (false, true, 5) => w!(b"+00000000T000000.00000+00:00"),
92 (false, true, 6) => w!(b"+00000000T000000.000000+00:00"),
93 (false, true, 7) => w!(b"+00000000T000000.0000000+00:00"),
94 (false, true, 8) => w!(b"+00000000T000000.00000000+00:00"),
95 (false, true, 9) => w!(b"+00000000T000000.000000000+00:00"),
96 (false, false, 0) => w!(b"+00000000T000000Z"),
97 (false, false, 1) => w!(b"+00000000T000000.0Z"),
98 (false, false, 2) => w!(b"+00000000T000000.00Z"),
99 (false, false, 3) => w!(b"+00000000T000000.000Z"),
100 (false, false, 4) => w!(b"+00000000T000000.0000Z"),
101 (false, false, 5) => w!(b"+00000000T000000.00000Z"),
102 (false, false, 6) => w!(b"+00000000T000000.000000Z"),
103 (false, false, 7) => w!(b"+00000000T000000.0000000Z"),
104 (false, false, 8) => w!(b"+00000000T000000.00000000Z"),
105 (false, false, 9) => w!(b"+00000000T000000.000000000Z"),
106
107 _ => unsafe { core::hint::unreachable_unchecked() },
109 }
110
111 value
112}
113
114#[derive(Clone, Copy)]
116#[repr(transparent)]
117pub struct TimestampStr<S: IsValidFormat>(pub(crate) S::Storage);
118
119impl<S: IsValidFormat> TimestampStr<S> {
120 pub const MAX_LEN: usize = <S::Length as t::Unsigned>::USIZE;
128}
129
130#[cfg(test)]
131mod ts_str_tests {
132 use crate::{formats as f, IsValidFormat, TimestampStr};
133 use core::mem::size_of;
134
135 #[test]
136 fn test_ts_str_size_of() {
137 fn assert_size_of<S: IsValidFormat>() {
138 assert_eq!(size_of::<TimestampStr<S>>(), <TimestampStr<S>>::MAX_LEN);
139 }
140
141 assert_size_of::<f::FullMicroseconds>();
142 assert_size_of::<f::FullMilliseconds>();
143 assert_size_of::<f::FullMillisecondsOffset>();
144 assert_size_of::<f::FullNanoseconds>();
145 assert_size_of::<f::ShortMilliseconds>();
146 }
147}
148
149impl<S: IsValidFormat> AsRef<str> for TimestampStr<S> {
150 #[inline]
151 fn as_ref(&self) -> &str {
152 unsafe {
155 let bytes = self.0.as_ref();
157 let is_positive = *bytes.get_unchecked(0) == b'+';
158 core::str::from_utf8_unchecked(bytes.get_unchecked(is_positive as usize..))
159 }
160 }
161}
162
163use core::borrow::Borrow;
164
165impl<S: IsValidFormat> Borrow<str> for TimestampStr<S> {
166 #[inline]
167 fn borrow(&self) -> &str {
168 self.as_ref()
169 }
170}
171
172use core::ops::Deref;
173
174impl<S: IsValidFormat> Deref for TimestampStr<S> {
175 type Target = str;
176
177 #[inline(always)]
178 fn deref(&self) -> &Self::Target {
179 self.as_ref()
180 }
181}
182
183impl<S: IsValidFormat> PartialEq for TimestampStr<S> {
184 #[inline]
185 fn eq(&self, other: &Self) -> bool {
186 self.0.as_ref() == other.0.as_ref()
187 }
188}
189
190impl<S: IsValidFormat> PartialEq<str> for TimestampStr<S> {
191 #[inline]
192 fn eq(&self, other: &str) -> bool {
193 self.as_ref() == other
194 }
195}
196
197impl<S: IsValidFormat> PartialEq<&str> for TimestampStr<S> {
198 #[inline]
199 fn eq(&self, other: &&str) -> bool {
200 self.as_ref() == *other
201 }
202}
203
204impl<S: IsValidFormat> PartialEq<TimestampStr<S>> for str {
205 #[inline]
206 fn eq(&self, other: &TimestampStr<S>) -> bool {
207 self == other.as_ref()
208 }
209}
210
211impl<S: IsValidFormat> PartialEq<TimestampStr<S>> for &str {
212 #[inline]
213 fn eq(&self, other: &TimestampStr<S>) -> bool {
214 *self == other.as_ref()
215 }
216}
217
218use core::fmt;
219
220impl<S: IsValidFormat> fmt::Debug for TimestampStr<S> {
221 #[inline(always)]
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 fmt::Debug::fmt(self.as_ref(), f)
224 }
225}
226
227impl<S: IsValidFormat> fmt::Display for TimestampStr<S> {
228 #[inline(always)]
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 fmt::Display::fmt(self.as_ref(), f)
231 }
232}
233
234#[cfg(feature = "serde")]
235mod serde_impl {
236 use serde_core::ser::{Serialize, Serializer};
237
238 use super::{IsValidFormat, TimestampStr};
239
240 impl<STORAGE: IsValidFormat> Serialize for TimestampStr<STORAGE> {
241 #[inline]
242 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
243 where
244 S: Serializer,
245 {
246 serializer.serialize_str(self)
247 }
248 }
249}