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