| 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 | |