Skip to main content

time/format_description/
owned_format_item.rs

1//! A format item with owned data.
2
3use alloc::boxed::Box;
4use alloc::string::String;
5use alloc::vec::Vec;
6use core::fmt;
7
8use crate::error;
9use crate::format_description::{BorrowedFormatItem, Component};
10
11/// A complete description of how to format and parse a type.
12#[non_exhaustive]
13#[derive(Clone, Eq)]
14pub enum OwnedFormatItem {
15    /// Bytes that are formatted as-is.
16    ///
17    /// **Note**: These bytes **should** be UTF-8, but are not required to be. The value is passed
18    /// through `String::from_utf8_lossy` when necessary.
19    #[deprecated(
20        since = "0.3.48",
21        note = "use `StringLiteral` instead; raw bytes are not recommended"
22    )]
23    Literal(Box<[u8]>),
24    /// A string that is formatted as-is.
25    StringLiteral(Box<str>),
26    /// A minimal representation of a single non-literal item.
27    Component(Component),
28    /// A series of literals or components that collectively form a partial or complete
29    /// description.
30    Compound(Box<[Self]>),
31    /// A `FormatItem` that may or may not be present when parsing. If parsing fails, there
32    /// will be no effect on the resulting `struct`.
33    ///
34    /// This variant has no effect on formatting, as the value is guaranteed to be present.
35    Optional(Box<Self>),
36    /// A series of `FormatItem`s where, when parsing, the first successful parse is used. When
37    /// formatting, the first element of the [`Vec`] is used. An empty [`Vec`] is a no-op when
38    /// formatting or parsing.
39    First(Box<[Self]>),
40}
41
42impl fmt::Debug for OwnedFormatItem {
43    #[inline]
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        match self {
46            #[expect(deprecated)]
47            Self::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)),
48            Self::StringLiteral(literal) => f.write_str(literal),
49            Self::Component(component) => component.fmt(f),
50            Self::Compound(compound) => compound.fmt(f),
51            Self::Optional(item) => f.debug_tuple("Optional").field(item).finish(),
52            Self::First(items) => f.debug_tuple("First").field(items).finish(),
53        }
54    }
55}
56
57impl PartialEq for OwnedFormatItem {
58    fn eq(&self, other: &Self) -> bool {
59        match (self, other) {
60            // trivial equality checks
61            #[expect(deprecated)]
62            (Self::Literal(a), Self::Literal(b)) => a == b,
63            (Self::StringLiteral(a), Self::StringLiteral(b)) => a == b,
64            (Self::Component(a), Self::Component(b)) => a == b,
65            (Self::Compound(a), Self::Compound(b)) => a == b,
66            (Self::Optional(a), Self::Optional(b)) => a == b,
67            (Self::First(a), Self::First(b)) => a == b,
68            // bytes vs string (back-compatibility)
69            #[expect(deprecated)]
70            (Self::Literal(a), Self::StringLiteral(b)) => &**a == b.as_bytes(),
71            #[expect(deprecated)]
72            (Self::StringLiteral(a), Self::Literal(b)) => a.as_bytes() == &**b,
73            _ => false,
74        }
75    }
76}
77
78impl From<BorrowedFormatItem<'_>> for OwnedFormatItem {
79    #[inline]
80    fn from(item: BorrowedFormatItem<'_>) -> Self {
81        (&item).into()
82    }
83}
84
85impl From<&BorrowedFormatItem<'_>> for OwnedFormatItem {
86    #[inline]
87    fn from(item: &BorrowedFormatItem<'_>) -> Self {
88        match item {
89            #[expect(deprecated)]
90            BorrowedFormatItem::Literal(literal) => {
91                Self::Literal(literal.to_vec().into_boxed_slice())
92            }
93            BorrowedFormatItem::StringLiteral(literal) => {
94                use alloc::borrow::ToOwned as _;
95                Self::StringLiteral((*literal).to_owned().into_boxed_str())
96            }
97            BorrowedFormatItem::Component(component) => Self::Component(*component),
98            BorrowedFormatItem::Compound(compound) => {
99                Self::Compound(compound.iter().cloned().map(Into::into).collect())
100            }
101            BorrowedFormatItem::Optional(item) => Self::Optional(Box::new((*item).into())),
102            BorrowedFormatItem::First(items) => {
103                Self::First(items.iter().cloned().map(Into::into).collect())
104            }
105        }
106    }
107}
108
109impl From<Vec<BorrowedFormatItem<'_>>> for OwnedFormatItem {
110    #[inline]
111    fn from(items: Vec<BorrowedFormatItem<'_>>) -> Self {
112        items.as_slice().into()
113    }
114}
115
116impl<'a, T> From<&T> for OwnedFormatItem
117where
118    T: AsRef<[BorrowedFormatItem<'a>]> + ?Sized,
119{
120    #[inline]
121    fn from(items: &T) -> Self {
122        Self::Compound(items.as_ref().iter().cloned().map(Into::into).collect())
123    }
124}
125
126impl From<Component> for OwnedFormatItem {
127    #[inline]
128    fn from(component: Component) -> Self {
129        Self::Component(component)
130    }
131}
132
133impl TryFrom<OwnedFormatItem> for Component {
134    type Error = error::DifferentVariant;
135
136    #[inline]
137    fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> {
138        match value {
139            OwnedFormatItem::Component(component) => Ok(component),
140            _ => Err(error::DifferentVariant),
141        }
142    }
143}
144
145impl From<Vec<Self>> for OwnedFormatItem {
146    #[inline]
147    fn from(items: Vec<Self>) -> Self {
148        Self::Compound(items.into_boxed_slice())
149    }
150}
151
152impl TryFrom<OwnedFormatItem> for Vec<OwnedFormatItem> {
153    type Error = error::DifferentVariant;
154
155    #[inline]
156    fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> {
157        match value {
158            OwnedFormatItem::Compound(items) => Ok(items.into_vec()),
159            _ => Err(error::DifferentVariant),
160        }
161    }
162}
163
164impl PartialEq<Component> for OwnedFormatItem {
165    #[inline]
166    fn eq(&self, rhs: &Component) -> bool {
167        matches!(self, Self::Component(component) if component == rhs)
168    }
169}
170
171impl PartialEq<OwnedFormatItem> for Component {
172    #[inline]
173    fn eq(&self, rhs: &OwnedFormatItem) -> bool {
174        rhs == self
175    }
176}
177
178impl PartialEq<&[Self]> for OwnedFormatItem {
179    #[inline]
180    fn eq(&self, rhs: &&[Self]) -> bool {
181        matches!(self, Self::Compound(compound) if &&**compound == rhs)
182    }
183}
184
185impl PartialEq<OwnedFormatItem> for &[OwnedFormatItem] {
186    #[inline]
187    fn eq(&self, rhs: &OwnedFormatItem) -> bool {
188        rhs == self
189    }
190}