time/format_description/well_known/iso8601/
adt_hack.rs1use core::num::NonZeroU8;
4
5#[cfg(feature = "formatting")]
6use super::Iso8601;
7use super::{Config, DateKind, FormattedComponents as FC, OffsetPrecision, TimePrecision};
8
9#[doc(hidden)]
12pub type DoNotRelyOnWhatThisIs = u128;
13
14pub type EncodedConfig = DoNotRelyOnWhatThisIs;
19
20#[cfg(feature = "formatting")]
21impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
22 const CONFIG: Config = Config::decode(CONFIG);
24 pub(crate) const FORMAT_DATE: bool = matches!(
26 Self::CONFIG.formatted_components,
27 FC::Date | FC::DateTime | FC::DateTimeOffset
28 );
29 pub(crate) const FORMAT_TIME: bool = matches!(
31 Self::CONFIG.formatted_components,
32 FC::Time | FC::DateTime | FC::DateTimeOffset | FC::TimeOffset
33 );
34 pub(crate) const FORMAT_OFFSET: bool = matches!(
36 Self::CONFIG.formatted_components,
37 FC::Offset | FC::DateTimeOffset | FC::TimeOffset
38 );
39 pub(crate) const YEAR_IS_SIX_DIGITS: bool = Self::CONFIG.year_is_six_digits;
41 pub(crate) const USE_SEPARATORS: bool = Self::CONFIG.use_separators;
43 pub(crate) const DATE_KIND: DateKind = Self::CONFIG.date_kind;
45 pub(crate) const TIME_PRECISION: TimePrecision = Self::CONFIG.time_precision;
47 pub(crate) const OFFSET_PRECISION: OffsetPrecision = Self::CONFIG.offset_precision;
49}
50
51impl Config {
52 pub const fn encode(&self) -> EncodedConfig {
57 let mut bytes = [0; EncodedConfig::BITS as usize / 8];
58
59 bytes[0] = match self.formatted_components {
60 FC::None => 0,
61 FC::Date => 1,
62 FC::Time => 2,
63 FC::Offset => 3,
64 FC::DateTime => 4,
65 FC::DateTimeOffset => 5,
66 FC::TimeOffset => 6,
67 };
68 bytes[1] = self.use_separators as _;
69 bytes[2] = self.year_is_six_digits as _;
70 bytes[3] = match self.date_kind {
71 DateKind::Calendar => 0,
72 DateKind::Week => 1,
73 DateKind::Ordinal => 2,
74 };
75 bytes[4] = match self.time_precision {
76 TimePrecision::Hour { .. } => 0,
77 TimePrecision::Minute { .. } => 1,
78 TimePrecision::Second { .. } => 2,
79 };
80 bytes[5] = match self.time_precision {
81 TimePrecision::Hour { decimal_digits }
82 | TimePrecision::Minute { decimal_digits }
83 | TimePrecision::Second { decimal_digits } => match decimal_digits {
84 None => 0,
85 Some(decimal_digits) => decimal_digits.get(),
86 },
87 };
88 bytes[6] = match self.offset_precision {
89 OffsetPrecision::Hour => 0,
90 OffsetPrecision::Minute => 1,
91 };
92
93 EncodedConfig::from_be_bytes(bytes)
94 }
95
96 pub(super) const fn decode(encoded: EncodedConfig) -> Self {
99 let bytes = encoded.to_be_bytes();
100
101 let formatted_components = match bytes[0] {
102 0 => FC::None,
103 1 => FC::Date,
104 2 => FC::Time,
105 3 => FC::Offset,
106 4 => FC::DateTime,
107 5 => FC::DateTimeOffset,
108 6 => FC::TimeOffset,
109 _ => panic!("invalid configuration"),
110 };
111 let use_separators = match bytes[1] {
112 0 => false,
113 1 => true,
114 _ => panic!("invalid configuration"),
115 };
116 let year_is_six_digits = match bytes[2] {
117 0 => false,
118 1 => true,
119 _ => panic!("invalid configuration"),
120 };
121 let date_kind = match bytes[3] {
122 0 => DateKind::Calendar,
123 1 => DateKind::Week,
124 2 => DateKind::Ordinal,
125 _ => panic!("invalid configuration"),
126 };
127 let time_precision = match bytes[4] {
128 0 => TimePrecision::Hour {
129 decimal_digits: NonZeroU8::new(bytes[5]),
130 },
131 1 => TimePrecision::Minute {
132 decimal_digits: NonZeroU8::new(bytes[5]),
133 },
134 2 => TimePrecision::Second {
135 decimal_digits: NonZeroU8::new(bytes[5]),
136 },
137 _ => panic!("invalid configuration"),
138 };
139 let offset_precision = match bytes[6] {
140 0 => OffsetPrecision::Hour,
141 1 => OffsetPrecision::Minute,
142 _ => panic!("invalid configuration"),
143 };
144
145 let mut idx = 7; while idx < EncodedConfig::BITS as usize / 8 {
148 assert!(bytes[idx] == 0, "invalid configuration");
149 idx += 1;
150 }
151
152 Self {
153 formatted_components,
154 use_separators,
155 year_is_six_digits,
156 date_kind,
157 time_precision,
158 offset_precision,
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 macro_rules! eq {
168 ($a:expr, $b:expr) => {{
169 let a = $a;
170 let b = $b;
171 a.formatted_components == b.formatted_components
172 && a.use_separators == b.use_separators
173 && a.year_is_six_digits == b.year_is_six_digits
174 && a.date_kind == b.date_kind
175 && a.time_precision == b.time_precision
176 && a.offset_precision == b.offset_precision
177 }};
178 }
179
180 #[test]
181 fn encoding_roundtrip() {
182 macro_rules! assert_roundtrip {
183 ($config:expr) => {
184 let config = $config;
185 let encoded = config.encode();
186 let decoded = Config::decode(encoded);
187 assert!(eq!(config, decoded));
188 };
189 }
190
191 assert_roundtrip!(Config::DEFAULT);
192 assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::None));
193 assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Date));
194 assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Time));
195 assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Offset));
196 assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::DateTime));
197 assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::DateTimeOffset));
198 assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::TimeOffset));
199 assert_roundtrip!(Config::DEFAULT.set_use_separators(false));
200 assert_roundtrip!(Config::DEFAULT.set_use_separators(true));
201 assert_roundtrip!(Config::DEFAULT.set_year_is_six_digits(false));
202 assert_roundtrip!(Config::DEFAULT.set_year_is_six_digits(true));
203 assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Calendar));
204 assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Week));
205 assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Ordinal));
206 assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Hour {
207 decimal_digits: None,
208 }));
209 assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute {
210 decimal_digits: None,
211 }));
212 assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second {
213 decimal_digits: None,
214 }));
215 assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Hour {
216 decimal_digits: NonZeroU8::new(1),
217 }));
218 assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute {
219 decimal_digits: NonZeroU8::new(1),
220 }));
221 assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second {
222 decimal_digits: NonZeroU8::new(1),
223 }));
224 assert_roundtrip!(Config::DEFAULT.set_offset_precision(OffsetPrecision::Hour));
225 assert_roundtrip!(Config::DEFAULT.set_offset_precision(OffsetPrecision::Minute));
226 }
227
228 macro_rules! assert_decode_fail {
229 ($encoding:expr) => {
230 assert!(std::panic::catch_unwind(|| {
231 Config::decode($encoding);
232 })
233 .is_err());
234 };
235 }
236
237 #[test]
238 fn decode_fail() {
239 assert_decode_fail!(0x07_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00);
240 assert_decode_fail!(0x00_02_00_00_00_00_00_00_00_00_00_00_00_00_00_00);
241 assert_decode_fail!(0x00_00_02_00_00_00_00_00_00_00_00_00_00_00_00_00);
242 assert_decode_fail!(0x00_00_00_03_00_00_00_00_00_00_00_00_00_00_00_00);
243 assert_decode_fail!(0x00_00_00_00_03_00_00_00_00_00_00_00_00_00_00_00);
244 assert_decode_fail!(0x00_00_00_00_00_00_02_00_00_00_00_00_00_00_00_00);
245 assert_decode_fail!(0x00_00_00_00_00_00_00_01_00_00_00_00_00_00_00_00);
246 }
247}