1use core::iter::Sum;
2use core::ops::{Add, Deref};
3
4use crate::format_description::well_known::iso8601::EncodedConfig;
5use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
6use crate::format_description::{BorrowedFormatItem, Component, OwnedFormatItem, modifier};
7
8#[derive(Debug)]
10pub(crate) struct Metadata {
11 pub(crate) max_bytes_needed: usize,
16 pub(crate) guaranteed_utf8: bool,
21}
22
23impl Default for Metadata {
24 #[inline]
25 fn default() -> Self {
26 Self {
27 max_bytes_needed: 0,
28 guaranteed_utf8: true,
29 }
30 }
31}
32
33impl Add for Metadata {
34 type Output = Self;
35
36 #[inline]
37 fn add(self, rhs: Self) -> Self::Output {
38 Self {
39 max_bytes_needed: self.max_bytes_needed + rhs.max_bytes_needed,
40 guaranteed_utf8: self.guaranteed_utf8 && rhs.guaranteed_utf8,
41 }
42 }
43}
44
45impl Sum for Metadata {
46 #[inline]
47 fn sum<I>(iter: I) -> Self
48 where
49 I: Iterator<Item = Self>,
50 {
51 iter.fold(Self::default(), Self::add)
52 }
53}
54
55pub(crate) trait ComputeMetadata {
57 fn compute_metadata(&self) -> Metadata;
59}
60
61impl ComputeMetadata for Rfc2822 {
62 #[inline]
63 fn compute_metadata(&self) -> Metadata {
64 Metadata {
65 max_bytes_needed: 31,
66 guaranteed_utf8: true,
67 }
68 }
69}
70
71impl ComputeMetadata for Rfc3339 {
72 #[inline]
73 fn compute_metadata(&self) -> Metadata {
74 Metadata {
75 max_bytes_needed: 35,
76 guaranteed_utf8: true,
77 }
78 }
79}
80
81impl<const CONFIG: EncodedConfig> ComputeMetadata for Iso8601<CONFIG> {
82 #[inline]
83 fn compute_metadata(&self) -> Metadata {
84 const {
85 use crate::format_description::well_known::iso8601::{
86 DateKind, OffsetPrecision, TimePrecision,
87 };
88
89 let date_width = if Self::FORMAT_DATE {
90 let year_width = if Self::YEAR_IS_SIX_DIGITS {
91 7 } else {
93 4 };
95 let num_dashes = match Self::DATE_KIND {
96 DateKind::Calendar if Self::USE_SEPARATORS => 2,
97 DateKind::Week | DateKind::Ordinal if Self::USE_SEPARATORS => 1,
98 DateKind::Calendar | DateKind::Week | DateKind::Ordinal => 0,
99 };
100 let part_of_year_width = match Self::DATE_KIND {
101 DateKind::Calendar => 4,
102 DateKind::Week => 4,
103 DateKind::Ordinal => 3,
104 };
105
106 year_width + num_dashes + part_of_year_width
107 } else {
108 0
109 };
110
111 let time_width = if Self::FORMAT_TIME {
112 let t_separator = (Self::USE_SEPARATORS || Self::FORMAT_DATE) as usize;
113 let num_colons = match Self::TIME_PRECISION {
114 TimePrecision::Minute { .. } if Self::USE_SEPARATORS => 1,
115 TimePrecision::Second { .. } if Self::USE_SEPARATORS => 2,
116 TimePrecision::Hour { .. }
117 | TimePrecision::Minute { .. }
118 | TimePrecision::Second { .. } => 0,
119 };
120 let pre_decimal_digits = match Self::TIME_PRECISION {
121 TimePrecision::Hour { .. } => 2,
122 TimePrecision::Minute { .. } => 4,
123 TimePrecision::Second { .. } => 6,
124 };
125 let fractional_bytes = match Self::TIME_PRECISION {
126 TimePrecision::Hour { decimal_digits }
127 | TimePrecision::Minute { decimal_digits }
128 | TimePrecision::Second { decimal_digits } => {
129 if let Some(digits) = decimal_digits {
130 1 + digits.get() as usize
132 } else {
133 0
134 }
135 }
136 };
137
138 t_separator + num_colons + pre_decimal_digits + fractional_bytes
139 } else {
140 0
141 };
142
143 let offset_width = if Self::FORMAT_OFFSET {
144 match Self::OFFSET_PRECISION {
145 OffsetPrecision::Hour => 3,
146 OffsetPrecision::Minute if Self::USE_SEPARATORS => 6,
147 OffsetPrecision::Minute => 5,
148 }
149 } else {
150 0
151 };
152
153 Metadata {
154 max_bytes_needed: date_width + time_width + offset_width,
155 guaranteed_utf8: true,
156 }
157 }
158 }
159}
160
161impl ComputeMetadata for BorrowedFormatItem<'_> {
162 #[inline]
163 fn compute_metadata(&self) -> Metadata {
164 match self {
165 #[expect(deprecated)]
166 Self::Literal(bytes) => Metadata {
167 max_bytes_needed: bytes.len(),
168 guaranteed_utf8: false,
169 },
170 Self::StringLiteral(s) => Metadata {
171 max_bytes_needed: s.len(),
172 guaranteed_utf8: true,
173 },
174 Self::Component(component) => component.compute_metadata(),
175 Self::Compound(borrowed_format_items) => borrowed_format_items.compute_metadata(),
176 Self::Optional(borrowed_format_item) => borrowed_format_item.compute_metadata(),
177 Self::First(borrowed_format_items) => borrowed_format_items
178 .first()
179 .map_or_else(Metadata::default, ComputeMetadata::compute_metadata),
180 }
181 }
182}
183
184impl ComputeMetadata for OwnedFormatItem {
185 #[inline]
186 fn compute_metadata(&self) -> Metadata {
187 match self {
188 #[expect(deprecated)]
189 Self::Literal(bytes) => Metadata {
190 max_bytes_needed: bytes.len(),
191 guaranteed_utf8: false,
192 },
193 Self::StringLiteral(s) => Metadata {
194 max_bytes_needed: s.len(),
195 guaranteed_utf8: true,
196 },
197 Self::Component(component) => component.compute_metadata(),
198 Self::Compound(owned_format_items) => owned_format_items.compute_metadata(),
199 Self::Optional(owned_format_item) => owned_format_item.compute_metadata(),
200 Self::First(owned_format_items) => owned_format_items
201 .first()
202 .map_or_else(Metadata::default, ComputeMetadata::compute_metadata),
203 }
204 }
205}
206
207impl ComputeMetadata for Component {
208 #[inline]
209 fn compute_metadata(&self) -> Metadata {
210 let max_bytes_needed = match self {
211 Self::Day(_) => 2,
212 Self::MonthShort(_) => 3,
213 Self::MonthLong(_) => 9,
214 Self::MonthNumerical(_) => 2,
215 Self::Ordinal(_) => 3,
216 Self::WeekdayShort(_) => 3,
217 Self::WeekdayLong(_) => 9,
218 Self::WeekdaySunday(_) | Self::WeekdayMonday(_) => 1,
219 Self::WeekNumberIso(_) | Self::WeekNumberSunday(_) | Self::WeekNumberMonday(_) => 2,
220 Self::CalendarYearFullExtendedRange(_) => 7,
221 Self::CalendarYearFullStandardRange(_) => 5,
222 Self::IsoYearFullExtendedRange(_) => 7,
223 Self::IsoYearFullStandardRange(_) => 5,
224 Self::CalendarYearCenturyExtendedRange(_) => 5,
225 Self::CalendarYearCenturyStandardRange(_) => 3,
226 Self::IsoYearCenturyExtendedRange(_) => 5,
227 Self::IsoYearCenturyStandardRange(_) => 3,
228 Self::CalendarYearLastTwo(_) => 2,
229 Self::IsoYearLastTwo(_) => 2,
230 Self::Hour12(_) | Self::Hour24(_) => 2,
231 Self::Minute(_) | Self::Period(_) | Self::Second(_) => 2,
232 Self::Subsecond(modifier) => match modifier.digits {
233 modifier::SubsecondDigits::One => 1,
234 modifier::SubsecondDigits::Two => 2,
235 modifier::SubsecondDigits::Three => 3,
236 modifier::SubsecondDigits::Four => 4,
237 modifier::SubsecondDigits::Five => 5,
238 modifier::SubsecondDigits::Six => 6,
239 modifier::SubsecondDigits::Seven => 7,
240 modifier::SubsecondDigits::Eight => 8,
241 modifier::SubsecondDigits::Nine => 9,
242 modifier::SubsecondDigits::OneOrMore => 9,
243 },
244 Self::OffsetHour(_) => 3,
245 Self::OffsetMinute(_) | Self::OffsetSecond(_) => 2,
246 #[cfg(feature = "large-dates")]
247 Self::UnixTimestampSecond(_) => 15,
248 #[cfg(not(feature = "large-dates"))]
249 Self::UnixTimestampSecond(_) => 13,
250 #[cfg(feature = "large-dates")]
251 Self::UnixTimestampMillisecond(_) => 18,
252 #[cfg(not(feature = "large-dates"))]
253 Self::UnixTimestampMillisecond(_) => 16,
254 #[cfg(feature = "large-dates")]
255 Self::UnixTimestampMicrosecond(_) => 21,
256 #[cfg(not(feature = "large-dates"))]
257 Self::UnixTimestampMicrosecond(_) => 19,
258 #[cfg(feature = "large-dates")]
259 Self::UnixTimestampNanosecond(_) => 24,
260 #[cfg(not(feature = "large-dates"))]
261 Self::UnixTimestampNanosecond(_) => 22,
262 Self::Ignore(_) | Self::End(_) => 0,
263
264 #[expect(deprecated)]
266 Self::Month(modifier) => match modifier.repr {
267 modifier::MonthRepr::Numerical => 2,
268 modifier::MonthRepr::Long => 9,
269 modifier::MonthRepr::Short => 3,
270 },
271 #[expect(deprecated)]
272 Self::Weekday(modifier) => match modifier.repr {
273 modifier::WeekdayRepr::Short => 3,
274 modifier::WeekdayRepr::Long => 9,
275 modifier::WeekdayRepr::Sunday | modifier::WeekdayRepr::Monday => 1,
276 },
277 #[expect(deprecated)]
278 Self::WeekNumber(_) => 2,
279 #[expect(deprecated)]
280 Self::Hour(_) => 2,
281 #[cfg(feature = "large-dates")]
282 #[expect(deprecated)]
283 Self::UnixTimestamp(modifier) => match modifier.precision {
284 modifier::UnixTimestampPrecision::Second => 15,
285 modifier::UnixTimestampPrecision::Millisecond => 18,
286 modifier::UnixTimestampPrecision::Microsecond => 21,
287 modifier::UnixTimestampPrecision::Nanosecond => 24,
288 },
289 #[cfg(not(feature = "large-dates"))]
290 #[expect(deprecated)]
291 Self::UnixTimestamp(modifier) => match modifier.precision {
292 modifier::UnixTimestampPrecision::Second => 13,
293 modifier::UnixTimestampPrecision::Millisecond => 16,
294 modifier::UnixTimestampPrecision::Microsecond => 19,
295 modifier::UnixTimestampPrecision::Nanosecond => 22,
296 },
297 #[cfg(feature = "large-dates")]
298 #[expect(deprecated)]
299 Self::Year(modifier) => match modifier.repr {
300 modifier::YearRepr::Full => 7,
301 modifier::YearRepr::Century => 5,
302 modifier::YearRepr::LastTwo => 2,
303 },
304 #[cfg(not(feature = "large-dates"))]
305 #[expect(deprecated)]
306 Self::Year(modifier) => match modifier.repr {
307 modifier::YearRepr::Full => 5,
308 modifier::YearRepr::Century => 3,
309 modifier::YearRepr::LastTwo => 2,
310 },
311 };
312
313 Metadata {
314 max_bytes_needed,
315 guaranteed_utf8: true,
316 }
317 }
318}
319
320impl<T> ComputeMetadata for [T]
321where
322 T: ComputeMetadata,
323{
324 #[inline]
325 fn compute_metadata(&self) -> Metadata {
326 self.iter().map(ComputeMetadata::compute_metadata).sum()
327 }
328}
329
330impl<T> ComputeMetadata for T
331where
332 T: Deref<Target: ComputeMetadata>,
333{
334 #[inline]
335 fn compute_metadata(&self) -> Metadata {
336 self.deref().compute_metadata()
337 }
338}