Skip to main content

time/format_description/
format_description_v3.rs

1//! A version 3 format description.
2//!
3//! Unlike versions 1 and 2, this is opaque so as to permit any changes necessary without breaking
4//! downstream users. Other than `FormatDescriptionV3`, all items are internal.
5
6#[cfg(feature = "alloc")]
7use alloc::boxed::Box;
8use core::fmt;
9
10use crate::format_description::modifier;
11
12/// A complete description of how to format and parse a type.
13///
14/// Both for forwards compatibility and to enable optimizations, this type is deliberately opaque
15/// and cannot be constructed by users of the crate. Instead, it is returned by the
16/// `format_description!` macro (when `version=3` is used) as well as the `parse_borrowed` and
17/// `parse_owned` methods.
18#[derive(Clone)]
19pub struct FormatDescriptionV3<'a> {
20    /// The inner `enum` that controls all business logic.
21    pub(crate) inner: FormatDescriptionV3Inner<'a>,
22    /// The maximum number of bytes that are needed to format any value using this format
23    /// description.
24    #[cfg(feature = "formatting")]
25    pub(crate) max_bytes_needed: usize,
26}
27
28impl fmt::Debug for FormatDescriptionV3<'_> {
29    #[inline]
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        self.inner.fmt(f)
32    }
33}
34
35impl FormatDescriptionV3<'_> {
36    /// Convert the format description to an owned version, enabling it to be stored without regard
37    /// for lifetime.
38    #[cfg(feature = "alloc")]
39    #[inline]
40    pub fn to_owned(self) -> FormatDescriptionV3<'static> {
41        FormatDescriptionV3 {
42            inner: self.inner.to_owned(),
43            #[cfg(feature = "formatting")]
44            max_bytes_needed: self.max_bytes_needed,
45        }
46    }
47}
48
49/// The inner `enum` of a version 3 format description. Controls all business logic.
50// public via `crate::format_description::__private` for macro use
51#[non_exhaustive]
52#[derive(Clone)]
53pub enum FormatDescriptionV3Inner<'a> {
54    /// A minimal representation of a single non-literal item.
55    Component(Component),
56    /// A string that is formatted as-is.
57    BorrowedLiteral(&'a str),
58    /// A series of literals or components that collectively form a partial or complete description.
59    BorrowedCompound(&'a [Self]),
60    /// An item that may or may not be present when parsing. If parsing fails, there will be no
61    /// effect on the resulting `struct`.
62    BorrowedOptional {
63        /// Whether the item should be formatted.
64        format: bool,
65        /// The item in question.
66        item: &'a Self,
67    },
68    /// A series of items where, when parsing, the first successful parse is used. When formatting,
69    /// the first item is used. If no items are present, both formatting and parsing are no-ops.
70    BorrowedFirst(&'a [Self]),
71    /// A string that is formatted as-is.
72    #[cfg(feature = "alloc")]
73    OwnedLiteral(Box<str>),
74    /// A series of literals or components that collectively form a partial or complete description.
75    #[cfg(feature = "alloc")]
76    OwnedCompound(Box<[Self]>),
77    /// An item that may or may not be present when parsing. If parsing fails, there will be no
78    /// effect on the resulting `struct`.
79    #[cfg(feature = "alloc")]
80    OwnedOptional {
81        /// Whether the item should be formatted.
82        format: bool,
83        /// The item in question.
84        item: Box<Self>,
85    },
86    /// A series of items where, when parsing, the first successful parse is used. When formatting,
87    /// the first item is used. If no items are present, both formatting and parsing are no-ops.
88    #[cfg(feature = "alloc")]
89    OwnedFirst(Box<[Self]>),
90}
91
92impl fmt::Debug for FormatDescriptionV3Inner<'_> {
93    #[inline]
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        match self {
96            Self::Component(component) => f.debug_tuple("Component").field(component).finish(),
97            Self::BorrowedLiteral(literal) => f.debug_tuple("Literal").field(literal).finish(),
98            Self::BorrowedCompound(compound) => f.debug_tuple("Compound").field(compound).finish(),
99            Self::BorrowedOptional {
100                format: should_format,
101                item,
102            } => f
103                .debug_struct("Optional")
104                .field("should_format", should_format)
105                .field("item", item)
106                .finish(),
107            Self::BorrowedFirst(items) => f.debug_tuple("First").field(items).finish(),
108            #[cfg(feature = "alloc")]
109            Self::OwnedLiteral(literal) => f.debug_tuple("Literal").field(literal).finish(),
110            #[cfg(feature = "alloc")]
111            Self::OwnedCompound(compound) => f.debug_tuple("Compound").field(compound).finish(),
112            #[cfg(feature = "alloc")]
113            Self::OwnedOptional {
114                format: should_format,
115                item,
116            } => f
117                .debug_struct("Optional")
118                .field("should_format", should_format)
119                .field("item", item)
120                .finish(),
121            #[cfg(feature = "alloc")]
122            Self::OwnedFirst(items) => f.debug_tuple("First").field(items).finish(),
123        }
124    }
125}
126
127impl<'a> FormatDescriptionV3Inner<'a> {
128    /// Convert the format description to an owned version, enabling it to be stored without regard
129    /// for lifetime.
130    #[cfg(feature = "alloc")]
131    fn to_owned(&self) -> FormatDescriptionV3Inner<'static> {
132        use alloc::borrow::ToOwned as _;
133        use alloc::boxed::Box;
134        use alloc::vec::Vec;
135
136        match self {
137            Self::Component(component) => FormatDescriptionV3Inner::Component(*component),
138            Self::BorrowedLiteral(literal) => {
139                FormatDescriptionV3Inner::OwnedLiteral((*literal).to_owned().into_boxed_str())
140            }
141            Self::BorrowedCompound(compound) => FormatDescriptionV3Inner::OwnedCompound(
142                compound
143                    .iter()
144                    .map(|v| v.to_owned())
145                    .collect::<Vec<_>>()
146                    .into_boxed_slice(),
147            ),
148            Self::BorrowedOptional { format, item } => FormatDescriptionV3Inner::OwnedOptional {
149                format: *format,
150                item: Box::new((*item).to_owned()),
151            },
152            Self::BorrowedFirst(items) => FormatDescriptionV3Inner::OwnedFirst(
153                items
154                    .iter()
155                    .map(|v| v.to_owned())
156                    .collect::<Vec<_>>()
157                    .into_boxed_slice(),
158            ),
159            Self::OwnedLiteral(literal) => FormatDescriptionV3Inner::OwnedLiteral(literal.clone()),
160            Self::OwnedCompound(compound) => FormatDescriptionV3Inner::OwnedCompound(
161                compound
162                    .into_iter()
163                    .map(|v| v.to_owned())
164                    .collect::<Vec<_>>()
165                    .into_boxed_slice(),
166            ),
167            Self::OwnedOptional { format, item } => FormatDescriptionV3Inner::OwnedOptional {
168                format: *format,
169                item: Box::new((**item).to_owned()),
170            },
171            Self::OwnedFirst(items) => FormatDescriptionV3Inner::OwnedFirst(
172                items
173                    .into_iter()
174                    .map(|v| v.to_owned())
175                    .collect::<Vec<_>>()
176                    .into_boxed_slice(),
177            ),
178        }
179    }
180
181    /// Convert the inner `enum` to a `FormatDescriptionV3`.
182    #[inline]
183    pub const fn into_opaque(self) -> FormatDescriptionV3<'a> {
184        FormatDescriptionV3 {
185            #[cfg(feature = "formatting")]
186            max_bytes_needed: self.max_bytes_needed(),
187            inner: self,
188        }
189    }
190
191    /// Obtain the maximum number of bytes that are needed to format any value using this format
192    /// description.
193    #[cfg(feature = "formatting")]
194    const fn max_bytes_needed(&self) -> usize {
195        match self {
196            FormatDescriptionV3Inner::Component(component) => component.max_bytes_needed(),
197            FormatDescriptionV3Inner::BorrowedLiteral(s) => s.len(),
198            FormatDescriptionV3Inner::BorrowedCompound(items) => {
199                let mut max_bytes_needed = 0;
200                let mut idx = 0;
201                while idx < items.len() {
202                    max_bytes_needed += items[idx].max_bytes_needed();
203                    idx += 1;
204                }
205                max_bytes_needed
206            }
207            FormatDescriptionV3Inner::BorrowedOptional { format, item } => {
208                if *format {
209                    item.max_bytes_needed()
210                } else {
211                    0
212                }
213            }
214            FormatDescriptionV3Inner::BorrowedFirst(items) => {
215                if items.is_empty() {
216                    0
217                } else {
218                    items[0].max_bytes_needed()
219                }
220            }
221            FormatDescriptionV3Inner::OwnedLiteral(s) => s.len(),
222            FormatDescriptionV3Inner::OwnedCompound(items) => {
223                let mut max_bytes_needed = 0;
224                let mut idx = 0;
225                while idx < items.len() {
226                    max_bytes_needed += items[idx].max_bytes_needed();
227                    idx += 1;
228                }
229                max_bytes_needed
230            }
231            FormatDescriptionV3Inner::OwnedOptional { format, item } => {
232                if *format {
233                    item.max_bytes_needed()
234                } else {
235                    0
236                }
237            }
238            FormatDescriptionV3Inner::OwnedFirst(items) => {
239                if items.is_empty() {
240                    0
241                } else {
242                    items[0].max_bytes_needed()
243                }
244            }
245        }
246    }
247}
248
249/// A component of a larger format description.
250// public via `crate::format_description::__private` for macro use
251#[non_exhaustive]
252#[derive(Debug, Clone, Copy)]
253pub enum Component {
254    /// Day of the month.
255    Day(modifier::Day),
256    /// Month of the year in the abbreviated form (e.g. "Jan").
257    MonthShort(modifier::MonthShort),
258    /// Month of the year in the full form (e.g. "January").
259    MonthLong(modifier::MonthLong),
260    /// Month of the year in the numerical form (e.g. "1" for January).
261    MonthNumerical(modifier::MonthNumerical),
262    /// Ordinal day of the year.
263    Ordinal(modifier::Ordinal),
264    /// Weekday in the abbreviated form (e.g. "Mon").
265    WeekdayShort(modifier::WeekdayShort),
266    /// Weekday in the full form (e.g. "Monday").
267    WeekdayLong(modifier::WeekdayLong),
268    /// Weekday number where Sunday is either 0 or 1 depending on the modifier.
269    WeekdaySunday(modifier::WeekdaySunday),
270    /// Weekday number where Monday is either 0 or 1 depending on the modifier.
271    WeekdayMonday(modifier::WeekdayMonday),
272    /// Week number of the year, where week 1 starts is the week beginning on Monday that contains
273    /// January 4.
274    WeekNumberIso(modifier::WeekNumberIso),
275    /// Week number of the year, where week 1 starts on the first Sunday of the calendar year.
276    WeekNumberSunday(modifier::WeekNumberSunday),
277    /// Week number of the year, where week 1 starts on the first Monday of the calendar year.
278    WeekNumberMonday(modifier::WeekNumberMonday),
279    /// The calendar year. Supports the extended range.
280    CalendarYearFullExtendedRange(modifier::CalendarYearFullExtendedRange),
281    /// The calendar year. Does not support the extended range.
282    CalendarYearFullStandardRange(modifier::CalendarYearFullStandardRange),
283    /// The ISO week-based year. Supports the extended range.
284    IsoYearFullExtendedRange(modifier::IsoYearFullExtendedRange),
285    /// The ISO week-based year. Does not support the extended range.
286    IsoYearFullStandardRange(modifier::IsoYearFullStandardRange),
287    /// The century of the calendar year. Supports the extended range.
288    CalendarYearCenturyExtendedRange(modifier::CalendarYearCenturyExtendedRange),
289    /// The century of the calendar year. Does not support the extended range.
290    CalendarYearCenturyStandardRange(modifier::CalendarYearCenturyStandardRange),
291    /// The century of the ISO week-based year. Supports the extended range.
292    IsoYearCenturyExtendedRange(modifier::IsoYearCenturyExtendedRange),
293    /// The century of the ISO week-based year. Does not support the extended range.
294    IsoYearCenturyStandardRange(modifier::IsoYearCenturyStandardRange),
295    /// The last two digits of the calendar year.
296    CalendarYearLastTwo(modifier::CalendarYearLastTwo),
297    /// The last two digits of the ISO week-based year.
298    IsoYearLastTwo(modifier::IsoYearLastTwo),
299    /// Hour of the day using the 12-hour clock.
300    Hour12(modifier::Hour12),
301    /// Hour of the day using the 24-hour clock.
302    Hour24(modifier::Hour24),
303    /// Minute within the hour.
304    Minute(modifier::Minute),
305    /// AM/PM part of the time.
306    Period(modifier::Period),
307    /// Second within the minute.
308    Second(modifier::Second),
309    /// Subsecond within the second.
310    Subsecond(modifier::Subsecond),
311    /// Hour of the UTC offset.
312    OffsetHour(modifier::OffsetHour),
313    /// Minute within the hour of the UTC offset.
314    OffsetMinute(modifier::OffsetMinute),
315    /// Second within the minute of the UTC offset.
316    OffsetSecond(modifier::OffsetSecond),
317    /// A number of bytes to ignore when parsing. This has no effect on formatting.
318    Ignore(modifier::Ignore),
319    /// A Unix timestamp in seconds.
320    UnixTimestampSecond(modifier::UnixTimestampSecond),
321    /// A Unix timestamp in milliseconds.
322    UnixTimestampMillisecond(modifier::UnixTimestampMillisecond),
323    /// A Unix timestamp in microseconds.
324    UnixTimestampMicrosecond(modifier::UnixTimestampMicrosecond),
325    /// A Unix timestamp in nanoseconds.
326    UnixTimestampNanosecond(modifier::UnixTimestampNanosecond),
327    /// The end of input. Parsing this component will fail if there is any input remaining. This
328    /// component neither affects formatting nor consumes any input when parsing.
329    End(modifier::End),
330}
331
332impl Component {
333    #[cfg(feature = "formatting")]
334    const fn max_bytes_needed(&self) -> usize {
335        match self {
336            Self::Day(_) => 2,
337            Self::MonthShort(_) => 3,
338            Self::MonthLong(_) => 9,
339            Self::MonthNumerical(_) => 2,
340            Self::Ordinal(_) => 3,
341            Self::WeekdayShort(_) => 3,
342            Self::WeekdayLong(_) => 9,
343            Self::WeekdaySunday(_) | Self::WeekdayMonday(_) => 1,
344            Self::WeekNumberIso(_) | Self::WeekNumberSunday(_) | Self::WeekNumberMonday(_) => 2,
345            Self::CalendarYearFullExtendedRange(_) => 7,
346            Self::CalendarYearFullStandardRange(_) => 5,
347            Self::IsoYearFullExtendedRange(_) => 7,
348            Self::IsoYearFullStandardRange(_) => 5,
349            Self::CalendarYearCenturyExtendedRange(_) => 5,
350            Self::CalendarYearCenturyStandardRange(_) => 3,
351            Self::IsoYearCenturyExtendedRange(_) => 5,
352            Self::IsoYearCenturyStandardRange(_) => 3,
353            Self::CalendarYearLastTwo(_) => 2,
354            Self::IsoYearLastTwo(_) => 2,
355            Self::Hour12(_) | Self::Hour24(_) => 2,
356            Self::Minute(_) | Self::Period(_) | Self::Second(_) => 2,
357            Self::Subsecond(modifier) => match modifier.digits {
358                modifier::SubsecondDigits::One => 1,
359                modifier::SubsecondDigits::Two => 2,
360                modifier::SubsecondDigits::Three => 3,
361                modifier::SubsecondDigits::Four => 4,
362                modifier::SubsecondDigits::Five => 5,
363                modifier::SubsecondDigits::Six => 6,
364                modifier::SubsecondDigits::Seven => 7,
365                modifier::SubsecondDigits::Eight => 8,
366                modifier::SubsecondDigits::Nine => 9,
367                modifier::SubsecondDigits::OneOrMore => 9,
368            },
369            Self::OffsetHour(_) => 3,
370            Self::OffsetMinute(_) | Self::OffsetSecond(_) => 2,
371            #[cfg(feature = "large-dates")]
372            Self::UnixTimestampSecond(_) => 15,
373            #[cfg(not(feature = "large-dates"))]
374            Self::UnixTimestampSecond(_) => 13,
375            #[cfg(feature = "large-dates")]
376            Self::UnixTimestampMillisecond(_) => 18,
377            #[cfg(not(feature = "large-dates"))]
378            Self::UnixTimestampMillisecond(_) => 16,
379            #[cfg(feature = "large-dates")]
380            Self::UnixTimestampMicrosecond(_) => 21,
381            #[cfg(not(feature = "large-dates"))]
382            Self::UnixTimestampMicrosecond(_) => 19,
383            #[cfg(feature = "large-dates")]
384            Self::UnixTimestampNanosecond(_) => 24,
385            #[cfg(not(feature = "large-dates"))]
386            Self::UnixTimestampNanosecond(_) => 22,
387            Self::Ignore(_) | Self::End(_) => 0,
388        }
389    }
390}