1use std::slice::SliceIndex;
2
3use thiserror::Error;
4use time::format_description::{modifier, Component, FormatItem};
5
6use super::desc_parser::Collector;
7
8#[derive(Error, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
9#[non_exhaustive]
10pub enum Error {
11 #[error("Unknown specifier `%{0}`")]
12 UnknownSpecifier(char),
13 #[error("No FormatItem that represent {0}")]
14 NoCorrespondingFormatItem(&'static str),
15}
16
17struct ToFormatItemCollector<'a> {
18 fmt: &'a [u8],
19 items: Vec<FormatItem<'a>>,
20}
21
22impl<'a> ToFormatItemCollector<'a> {
23 fn new(fmt: &'a [u8]) -> Self {
24 Self {
25 fmt,
26 items: Default::default(),
27 }
28 }
29}
30
31macro_rules! all_paddings {
32 ($ret: expr, $create_base: expr, $component_builder: expr) => {
33 const fn with_padding(pad: modifier::Padding) -> Component {
34 let mut m = $create_base;
35 m.padding = pad;
36 $component_builder(m)
37 }
38 static ITEMS: [FormatItem; 3] = [
39 FormatItem::Component(with_padding(modifier::Padding::Zero)),
40 FormatItem::Component(with_padding(modifier::Padding::Space)),
41 FormatItem::Component(with_padding(modifier::Padding::None)),
42 ];
43 $ret.push(FormatItem::First(&ITEMS));
44 };
45}
46
47impl<'a> Collector for ToFormatItemCollector<'a> {
48 type Output = Vec<FormatItem<'a>>;
49 type Error = Error;
50
51 #[inline]
52 fn spaces(&mut self) -> Result<(), Self::Error> {
53 self.items.push(FormatItem::Optional(&FormatItem::First(&[
55 FormatItem::Literal(b" "),
56 FormatItem::Literal(b"\n"),
57 FormatItem::Literal(b"\t"),
58 ])));
59 Ok(())
60 }
61
62 #[inline]
63 fn day_of_week_name(&mut self) -> Result<(), Self::Error> {
64 const fn short() -> FormatItem<'static> {
65 let mut short = modifier::Weekday::default();
66 short.repr = modifier::WeekdayRepr::Short;
67 short.case_sensitive = false;
68 FormatItem::Component(Component::Weekday(short))
69 }
70 const fn long() -> FormatItem<'static> {
71 let mut long = modifier::Weekday::default();
72 long.repr = modifier::WeekdayRepr::Long;
73 long.case_sensitive = false;
74 FormatItem::Component(Component::Weekday(long))
75 }
76 static ITEMS: [FormatItem; 2] = [long(), short()];
77 self.items.push(FormatItem::First(&ITEMS));
78 Ok(())
79 }
80
81 #[inline]
82 fn month_name(&mut self) -> Result<(), Self::Error> {
83 const fn short() -> FormatItem<'static> {
84 let mut short = modifier::Month::default();
85 short.repr = modifier::MonthRepr::Short;
86 short.case_sensitive = false;
87 FormatItem::Component(Component::Month(short))
88 }
89 const fn long() -> FormatItem<'static> {
90 let mut long = modifier::Month::default();
91 long.repr = modifier::MonthRepr::Long;
92 long.case_sensitive = false;
93 FormatItem::Component(Component::Month(long))
94 }
95 static ITEMS: [FormatItem; 2] = [long(), short()];
96 self.items.push(FormatItem::First(&ITEMS));
97 Ok(())
98 }
99
100 #[inline]
101 fn year_prefix(&mut self) -> Result<(), Self::Error> {
102 Err(Self::Error::NoCorrespondingFormatItem("%C"))
103 }
104
105 #[inline]
106 fn day_of_month(&mut self) -> Result<(), Self::Error> {
107 all_paddings!(self.items, modifier::Day::default(), Component::Day);
108 Ok(())
109 }
110
111 #[inline]
112 fn hour_of_day(&mut self) -> Result<(), Self::Error> {
113 all_paddings!(self.items, modifier::Hour::default(), Component::Hour);
114 Ok(())
115 }
116
117 #[inline]
118 fn hour_of_day_12(&mut self) -> Result<(), Self::Error> {
119 all_paddings!(
120 self.items,
121 {
122 let mut base = modifier::Hour::default();
123 base.is_12_hour_clock = true;
124 base
125 },
126 Component::Hour
127 );
128 Ok(())
129 }
130
131 #[inline]
132 fn day_of_year(&mut self) -> Result<(), Self::Error> {
133 all_paddings!(self.items, modifier::Ordinal::default(), Component::Ordinal);
134 Ok(())
135 }
136
137 #[inline]
138 fn month_of_year(&mut self) -> Result<(), Self::Error> {
139 all_paddings!(self.items, modifier::Month::default(), Component::Month);
140 Ok(())
141 }
142
143 #[inline]
144 fn minute_of_hour(&mut self) -> Result<(), Self::Error> {
145 all_paddings!(self.items, modifier::Minute::default(), Component::Minute);
146 Ok(())
147 }
148
149 #[inline]
150 fn ampm(&mut self) -> Result<(), Self::Error> {
151 let mut modifier = modifier::Period::default();
152 modifier.case_sensitive = false;
153 self.items
154 .push(FormatItem::Component(Component::Period(modifier)));
155 Ok(())
156 }
157
158 #[inline]
159 fn second_of_minute(&mut self) -> Result<(), Self::Error> {
160 all_paddings!(self.items, modifier::Second::default(), Component::Second);
161 Ok(())
162 }
163
164 #[inline]
165 fn nanosecond_of_second(&mut self) -> Result<(), Self::Error> {
166 let modifier = modifier::Subsecond::default();
167 self.items
168 .push(FormatItem::Component(Component::Subsecond(modifier)));
169 Ok(())
170 }
171
172 #[inline]
173 fn tab(&mut self) -> Result<(), Self::Error> {
174 self.spaces()
175 }
176
177 #[inline]
178 fn week_number_of_current_year_start_sunday(&mut self) -> Result<(), Self::Error> {
179 all_paddings!(
180 self.items,
181 {
182 let mut base = modifier::WeekNumber::default();
183 base.repr = modifier::WeekNumberRepr::Sunday;
184 base
185 },
186 Component::WeekNumber
187 );
188 Ok(())
189 }
190
191 #[inline]
192 fn day_of_week_from_sunday_as_0(&mut self) -> Result<(), Self::Error> {
193 let mut modifier = modifier::Weekday::default();
194 modifier.repr = modifier::WeekdayRepr::Sunday;
195 modifier.one_indexed = false;
196 self.items
197 .push(FormatItem::Component(Component::Weekday(modifier)));
198 Ok(())
199 }
200
201 #[inline]
202 fn week_number_of_current_year_start_monday(&mut self) -> Result<(), Self::Error> {
203 all_paddings!(
204 self.items,
205 {
206 let mut base = modifier::WeekNumber::default();
207 base.repr = modifier::WeekNumberRepr::Monday;
208 base
209 },
210 Component::WeekNumber
211 );
212 Ok(())
213 }
214
215 #[inline]
216 fn year_suffix(&mut self) -> Result<(), Self::Error> {
217 all_paddings!(
218 self.items,
219 {
220 let mut base = modifier::Year::default();
221 base.repr = modifier::YearRepr::LastTwo;
222 base
223 },
224 Component::Year
225 );
226 Ok(())
227 }
228
229 #[inline]
230 fn year(&mut self) -> Result<(), Self::Error> {
231 all_paddings!(self.items, modifier::Year::default(), Component::Year);
232 Ok(())
233 }
234
235 #[inline]
236 fn timezone(&mut self) -> Result<(), Self::Error> {
237 let mut modifier = modifier::OffsetHour::default();
238 modifier.sign_is_mandatory = true;
239 self.items
240 .push(FormatItem::Component(Component::OffsetHour(modifier)));
241 self.items
242 .push(FormatItem::Optional(&FormatItem::Literal(b":")));
243 let modifier = modifier::OffsetMinute::default();
244 self.items
245 .push(FormatItem::Component(Component::OffsetMinute(modifier)));
246 Ok(())
247 }
248
249 #[inline]
250 fn timezone_name(&mut self) -> Result<(), Self::Error> {
251 Err(Self::Error::NoCorrespondingFormatItem("timezone name"))
252 }
253
254 #[inline]
255 fn static_str(&mut self, s: &'static str) -> Result<(), Self::Error> {
256 self.items.push(FormatItem::Literal(s.as_bytes()));
257 Ok(())
258 }
259
260 #[inline]
261 fn literal(
262 &mut self,
263 _lit: &str,
264 fmt_span: impl SliceIndex<[u8], Output = [u8]>,
265 ) -> Result<(), Self::Error> {
266 self.items.push(FormatItem::Literal(&self.fmt[fmt_span]));
267 Ok(())
268 }
269
270 #[inline]
271 fn unknown(&mut self, specifier: char) -> Result<(), Self::Error> {
272 Err(Self::Error::UnknownSpecifier(specifier))
273 }
274
275 #[inline]
276 fn unconsumed_input(&self) -> Result<(), Self::Error> {
277 Ok(())
278 }
279
280 #[inline]
281 fn output(self) -> Result<Self::Output, Self::Error> {
282 Ok(self.items)
283 }
284}
285
286pub fn parse_to_format_item(fmt: &str) -> Result<Vec<FormatItem>, Error> {
287 let collector = ToFormatItemCollector::new(fmt.as_bytes());
288 super::desc_parser::parse_format_specifications(fmt, collector, false)
289}
290
291#[cfg(test)]
292mod tests {
293 use time::{macros::datetime, OffsetDateTime, PrimitiveDateTime};
294
295 use super::parse_to_format_item;
296
297 #[test]
298 fn it_works() -> Result<(), super::Error> {
299 assert_eq!(
300 parse_to_format_item("%Y-%m-%d")?,
301 parse_to_format_item("%F")?,
302 );
303 Ok(())
304 }
305
306 #[test]
307 fn parse_primitive_datetime() -> Result<(), Box<dyn std::error::Error>> {
308 let format_items = parse_to_format_item("%Y-%m-%d %H:%M:%S")?;
309 assert_eq!(
310 PrimitiveDateTime::parse("2012-05-21 12:09:14", &format_items)?,
311 datetime!(2012-05-21 12:09:14)
312 );
313 Ok(())
314 }
315
316 #[test]
317 fn parse_offset_datetime() -> Result<(), Box<dyn std::error::Error>> {
318 let format_items = parse_to_format_item("%Y-%m-%d %H:%M:%S %z")?;
319 assert_eq!(
320 OffsetDateTime::parse("2012-05-21 12:09:14 +0900", &format_items)?,
321 datetime!(2012-05-21 12:09:14 +9:00)
322 );
323 assert_eq!(
324 OffsetDateTime::parse("2012-05-21 12:09:14 +09:00", &format_items)?,
325 datetime!(2012-05-21 12:09:14 +9:00)
326 );
327 Ok(())
328 }
329}