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