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