//! A format item with owned data. use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use core::fmt; use crate::error; use crate::format_description::{Component, FormatItem}; /// A complete description of how to format and parse a type. #[non_exhaustive] #[derive(Clone, PartialEq, Eq)] pub enum OwnedFormatItem { /// Bytes that are formatted as-is. /// /// **Note**: If you call the `format` method that returns a `String`, these bytes will be /// passed through `String::from_utf8_lossy`. Literal(Box<[u8]>), /// A minimal representation of a single non-literal item. Component(Component), /// A series of literals or components that collectively form a partial or complete /// description. Compound(Box<[Self]>), /// A `FormatItem` that may or may not be present when parsing. If parsing fails, there /// will be no effect on the resulting `struct`. /// /// This variant has no effect on formatting, as the value is guaranteed to be present. Optional(Box), /// A series of `FormatItem`s where, when parsing, the first successful parse is used. When /// formatting, the first element of the [`Vec`] is used. An empty [`Vec`] is a no-op when /// formatting or parsing. First(Box<[Self]>), } impl fmt::Debug for OwnedFormatItem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)), Self::Component(component) => component.fmt(f), Self::Compound(compound) => compound.fmt(f), Self::Optional(item) => f.debug_tuple("Optional").field(item).finish(), Self::First(items) => f.debug_tuple("First").field(items).finish(), } } } // region: conversions from FormatItem impl From> for OwnedFormatItem { fn from(item: FormatItem<'_>) -> Self { (&item).into() } } impl From<&FormatItem<'_>> for OwnedFormatItem { fn from(item: &FormatItem<'_>) -> Self { match item { FormatItem::Literal(literal) => Self::Literal(literal.to_vec().into_boxed_slice()), FormatItem::Component(component) => Self::Component(*component), FormatItem::Compound(compound) => Self::Compound( compound .iter() .cloned() .map(Into::into) .collect::>() .into_boxed_slice(), ), FormatItem::Optional(item) => Self::Optional(Box::new((*item).into())), FormatItem::First(items) => Self::First( items .iter() .cloned() .map(Into::into) .collect::>() .into_boxed_slice(), ), } } } impl From>> for OwnedFormatItem { fn from(items: Vec>) -> Self { items.as_slice().into() } } impl<'a, T: AsRef<[FormatItem<'a>]> + ?Sized> From<&T> for OwnedFormatItem { fn from(items: &T) -> Self { Self::Compound( items .as_ref() .iter() .cloned() .map(Into::into) .collect::>() .into_boxed_slice(), ) } } // endregion conversions from FormatItem // region: from variants impl From for OwnedFormatItem { fn from(component: Component) -> Self { Self::Component(component) } } impl TryFrom for Component { type Error = error::DifferentVariant; fn try_from(value: OwnedFormatItem) -> Result { match value { OwnedFormatItem::Component(component) => Ok(component), _ => Err(error::DifferentVariant), } } } impl From> for OwnedFormatItem { fn from(items: Vec) -> Self { Self::Compound(items.into_boxed_slice()) } } impl TryFrom for Vec { type Error = error::DifferentVariant; fn try_from(value: OwnedFormatItem) -> Result { match value { OwnedFormatItem::Compound(items) => Ok(items.into_vec()), _ => Err(error::DifferentVariant), } } } // endregion from variants // region: equality impl PartialEq for OwnedFormatItem { fn eq(&self, rhs: &Component) -> bool { matches!(self, Self::Component(component) if component == rhs) } } impl PartialEq for Component { fn eq(&self, rhs: &OwnedFormatItem) -> bool { rhs == self } } impl PartialEq<&[Self]> for OwnedFormatItem { fn eq(&self, rhs: &&[Self]) -> bool { matches!(self, Self::Compound(compound) if &&**compound == rhs) } } impl PartialEq for &[OwnedFormatItem] { fn eq(&self, rhs: &OwnedFormatItem) -> bool { rhs == self } } // endregion equality