1use std::slice::SliceIndex;
2
3pub(crate) trait Collector {
8 type Output;
9 type Error;
10 fn day_of_week_name_short(&mut self) -> Result<(), Self::Error>;
12 fn day_of_week_name_long(&mut self) -> Result<(), Self::Error>;
14 fn month_name_short(&mut self) -> Result<(), Self::Error>;
16 fn month_name_long(&mut self) -> Result<(), Self::Error>;
18 #[inline]
20 fn preferred_date_time(&mut self) -> Result<(), Self::Error> {
21 self.day_of_week_name_short()?;
22 self.static_str(" ")?;
23 self.month_name_short()?;
24 self.static_str(" ")?;
25 self.day_of_month_blank()?;
26 self.static_str(" ")?;
27 self.time_of_day()?;
28 self.static_str(" ")?;
29 self.year()
30 }
31 fn year_prefix(&mut self) -> Result<(), Self::Error>;
33 fn day_of_month(&mut self) -> Result<(), Self::Error>;
35 #[inline]
37 fn date_mmddyy_slash(&mut self) -> Result<(), Self::Error> {
38 self.month_of_year()?;
39 self.static_str("/")?;
40 self.day_of_month()?;
41 self.static_str("/")?;
42 self.year_suffix()
43 }
44 fn day_of_month_blank(&mut self) -> Result<(), Self::Error>;
46 #[inline]
48 fn date_yyyymmdd_hyphen(&mut self) -> Result<(), Self::Error> {
49 self.year()?;
50 self.static_str("-")?;
51 self.month_of_year()?;
52 self.static_str("-")?;
53 self.day_of_month()
54 }
55 fn iso8601_week_based_year_suffix(&mut self) -> Result<(), Self::Error>;
57 fn iso8601_week_based_year(&mut self) -> Result<(), Self::Error>;
59 fn hour_of_day(&mut self) -> Result<(), Self::Error>;
61 fn hour_of_day_12(&mut self) -> Result<(), Self::Error>;
63 fn day_of_year(&mut self) -> Result<(), Self::Error>;
65 fn hour_of_day_blank(&mut self) -> Result<(), Self::Error>;
67 fn hour_of_day_12_blank(&mut self) -> Result<(), Self::Error>;
69 fn month_of_year(&mut self) -> Result<(), Self::Error>;
71 fn minute_of_hour(&mut self) -> Result<(), Self::Error>;
73 #[inline]
75 fn new_line(&mut self) -> Result<(), Self::Error> {
76 self.static_str("\n")
77 }
78 fn ampm(&mut self) -> Result<(), Self::Error>;
80 fn ampm_lower(&mut self) -> Result<(), Self::Error>;
82 #[inline]
84 fn time_ampm(&mut self) -> Result<(), Self::Error> {
85 self.hour_of_day_12()?;
86 self.static_str(":")?;
87 self.minute_of_hour()?;
88 self.static_str(":")?;
89 self.second_of_minute()?;
90 self.static_str(" ")?;
91 self.ampm()
92 }
93 #[inline]
95 fn hour_minute_of_day(&mut self) -> Result<(), Self::Error> {
96 self.hour_of_day()?;
97 self.static_str(":")?;
98 self.minute_of_hour()
99 }
100 fn second_of_minute(&mut self) -> Result<(), Self::Error>;
102 fn nanosecond_of_second(&mut self) -> Result<(), Self::Error>;
104 #[inline]
106 fn tab(&mut self) -> Result<(), Self::Error> {
107 self.static_str("\t")
108 }
109 #[inline]
111 fn time_of_day(&mut self) -> Result<(), Self::Error> {
112 self.hour_of_day()?;
113 self.static_str(":")?;
114 self.minute_of_hour()?;
115 self.static_str(":")?;
116 self.second_of_minute()
117 }
118 fn day_of_week_from_monday_as_1(&mut self) -> Result<(), Self::Error>;
120 fn week_number_of_current_year_start_sunday(&mut self) -> Result<(), Self::Error>;
122 fn iso8601_week_number(&mut self) -> Result<(), Self::Error>;
124 fn day_of_week_from_sunday_as_0(&mut self) -> Result<(), Self::Error>;
126 fn week_number_of_current_year_start_monday(&mut self) -> Result<(), Self::Error>;
128 #[inline]
130 fn preferred_date(&mut self) -> Result<(), Self::Error> {
131 self.month_of_year()?;
132 self.static_str("/")?;
133 self.day_of_month()?;
134 self.static_str("/")?;
135 self.year_suffix()
136 }
137 #[inline]
139 fn preferred_time_of_day(&mut self) -> Result<(), Self::Error> {
140 self.hour_of_day()?;
141 self.static_str(":")?;
142 self.minute_of_hour()?;
143 self.static_str(":")?;
144 self.second_of_minute()
145 }
146 fn year_suffix(&mut self) -> Result<(), Self::Error>;
148 fn year(&mut self) -> Result<(), Self::Error>;
150 fn timezone(&mut self) -> Result<(), Self::Error>;
152 fn timezone_name(&mut self) -> Result<(), Self::Error>;
154 #[inline]
156 fn percent(&mut self) -> Result<(), Self::Error> {
157 self.static_str("%")
158 }
159 fn static_str(&mut self, s: &'static str) -> Result<(), Self::Error>;
162 fn literal(
167 &mut self,
168 lit: &str,
169 fmt_span: impl SliceIndex<[u8], Output = [u8]>,
170 ) -> Result<(), Self::Error>;
171 fn unknown(&mut self, specifier: char) -> Result<(), Self::Error>;
173
174 fn output(self) -> Result<Self::Output, Self::Error>;
176}
177
178pub(crate) fn parse_conversion_specifications<C: Collector>(
179 mut format: &str,
180 mut collector: C,
181) -> Result<C::Output, C::Error> {
182 let original_len = format.len();
183 while !format.is_empty() {
184 let i = format
185 .bytes()
186 .position(|c| c == b'%')
187 .unwrap_or(format.len());
188 if i > 0 {
189 let start = original_len - format.len();
190 let (lit, rest) = format.split_at(i);
191 collector.literal(lit, start..(start + i))?;
192 format = rest;
193 if format.is_empty() {
194 break;
195 }
196 }
197 assert_eq!(format.as_bytes()[0], b'%');
198 format = &format[1..];
199 if let Some(b) = format.bytes().next() {
200 match b {
201 b'a' => collector.day_of_week_name_short()?,
202 b'A' => collector.day_of_week_name_long()?,
203 b'b' | b'h' => collector.month_name_short()?,
204 b'B' => collector.month_name_long()?,
205 b'c' => collector.preferred_date_time()?,
206 b'C' => collector.year_prefix()?,
207 b'd' => collector.day_of_month()?,
208 b'D' => collector.date_mmddyy_slash()?,
209 b'e' => collector.day_of_month_blank()?,
210 b'F' => collector.date_yyyymmdd_hyphen()?,
211 b'g' => collector.iso8601_week_based_year_suffix()?,
212 b'G' => collector.iso8601_week_based_year()?,
213 b'H' => collector.hour_of_day()?,
214 b'I' => collector.hour_of_day_12()?,
215 b'j' => collector.day_of_year()?,
216 b'k' => collector.hour_of_day_blank()?,
217 b'l' => collector.hour_of_day_12_blank()?,
218 b'm' => collector.month_of_year()?,
219 b'M' => collector.minute_of_hour()?,
220 b'n' => collector.new_line()?,
221 b'p' => collector.ampm()?,
222 b'P' => collector.ampm_lower()?,
223 b'r' => collector.time_ampm()?,
224 b'R' => collector.hour_minute_of_day()?,
225 b'S' => collector.second_of_minute()?,
226 b'f' => collector.nanosecond_of_second()?,
227 b't' => collector.tab()?,
228 b'T' => collector.time_of_day()?,
229 b'u' => collector.day_of_week_from_monday_as_1()?,
230 b'U' => collector.week_number_of_current_year_start_sunday()?,
231 b'V' => collector.iso8601_week_number()?,
232 b'w' => collector.day_of_week_from_sunday_as_0()?,
233 b'W' => collector.week_number_of_current_year_start_monday()?,
234 b'x' => collector.preferred_date()?,
235 b'X' => collector.preferred_time_of_day()?,
236 b'y' => collector.year_suffix()?,
237 b'Y' => collector.year()?,
238 b'z' => collector.timezone()?,
239 b'Z' => collector.timezone_name()?,
240 b'%' => collector.percent()?,
241 _ => {
242 let c = format.chars().next().unwrap();
243 collector.unknown(c)?;
244 format = &format[c.len_utf8()..];
245 continue;
246 }
247 }
248 format = &format[1..];
249 } else {
250 collector.percent()?;
251 }
252 }
253 collector.output()
254}