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) => Self::Compound(
99                compound
100                    .iter()
101                    .cloned()
102                    .map(Into::into)
103                    .collect::<Vec<_>>()
104                    .into_boxed_slice(),
105            ),
106            BorrowedFormatItem::Optional(item) => Self::Optional(Box::new((*item).into())),
107            BorrowedFormatItem::First(items) => Self::First(
108                items
109                    .iter()
110                    .cloned()
111                    .map(Into::into)
112                    .collect::<Vec<_>>()
113                    .into_boxed_slice(),
114            ),
115        }
116    }
117}
118
119impl From<Vec<BorrowedFormatItem<'_>>> for OwnedFormatItem {
120    #[inline]
121    fn from(items: Vec<BorrowedFormatItem<'_>>) -> Self {
122        items.as_slice().into()
123    }
124}
125
126impl<'a, T> From<&T> for OwnedFormatItem
127where
128    T: AsRef<[BorrowedFormatItem<'a>]> + ?Sized,
129{
130    #[inline]
131    fn from(items: &T) -> Self {
132        Self::Compound(
133            items
134                .as_ref()
135                .iter()
136                .cloned()
137                .map(Into::into)
138                .collect::<Vec<_>>()
139                .into_boxed_slice(),
140        )
141    }
142}
143
144impl From<Component> for OwnedFormatItem {
145    #[inline]
146    fn from(component: Component) -> Self {
147        Self::Component(component)
148    }
149}
150
151impl TryFrom<OwnedFormatItem> for Component {
152    type Error = error::DifferentVariant;
153
154    #[inline]
155    fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> {
156        match value {
157            OwnedFormatItem::Component(component) => Ok(component),
158            _ => Err(error::DifferentVariant),
159        }
160    }
161}
162
163impl From<Vec<Self>> for OwnedFormatItem {
164    #[inline]
165    fn from(items: Vec<Self>) -> Self {
166        Self::Compound(items.into_boxed_slice())
167    }
168}
169
170impl TryFrom<OwnedFormatItem> for Vec<OwnedFormatItem> {
171    type Error = error::DifferentVariant;
172
173    #[inline]
174    fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> {
175        match value {
176            OwnedFormatItem::Compound(items) => Ok(items.into_vec()),
177            _ => Err(error::DifferentVariant),
178        }
179    }
180}
181
182impl PartialEq<Component> for OwnedFormatItem {
183    #[inline]
184    fn eq(&self, rhs: &Component) -> bool {
185        matches!(self, Self::Component(component) if component == rhs)
186    }
187}
188
189impl PartialEq<OwnedFormatItem> for Component {
190    #[inline]
191    fn eq(&self, rhs: &OwnedFormatItem) -> bool {
192        rhs == self
193    }
194}
195
196impl PartialEq<&[Self]> for OwnedFormatItem {
197    #[inline]
198    fn eq(&self, rhs: &&[Self]) -> bool {
199        matches!(self, Self::Compound(compound) if &&**compound == rhs)
200    }
201}
202
203impl PartialEq<OwnedFormatItem> for &[OwnedFormatItem] {
204    #[inline]
205    fn eq(&self, rhs: &OwnedFormatItem) -> bool {
206        rhs == self
207    }
208}