diff --git a/time/src/format_description/borrowed_format_item.rs b/time/src/format_description/borrowed_format_item.rs new file mode 100644 index 000000000..7657f19c2 --- /dev/null +++ b/time/src/format_description/borrowed_format_item.rs @@ -0,0 +1,105 @@ +//! A format item with borrowed data. + +use alloc::string::String; +#[cfg(feature = "alloc")] +use core::fmt; + +use crate::error; +use crate::format_description::Component; + +/// A complete description of how to format and parse a type. +#[non_exhaustive] +#[cfg_attr(not(feature = "alloc"), derive(Debug))] +#[derive(Clone, PartialEq, Eq)] +pub enum BorrowedFormatItem<'a> { + /// 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(&'a [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(&'a [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(&'a Self), + /// A series of `FormatItem`s where, when parsing, the first successful parse is used. When + /// formatting, the first element of the slice is used. An empty slice is a no-op when + /// formatting or parsing. + First(&'a [Self]), +} + +#[cfg(feature = "alloc")] +impl fmt::Debug for BorrowedFormatItem<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BorrowedFormatItem::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)), + BorrowedFormatItem::Component(component) => component.fmt(f), + BorrowedFormatItem::Compound(compound) => compound.fmt(f), + BorrowedFormatItem::Optional(item) => f.debug_tuple("Optional").field(item).finish(), + BorrowedFormatItem::First(items) => f.debug_tuple("First").field(items).finish(), + } + } +} + +impl From for BorrowedFormatItem<'_> { + fn from(component: Component) -> Self { + Self::Component(component) + } +} + +impl TryFrom> for Component { + type Error = error::DifferentVariant; + + fn try_from(value: BorrowedFormatItem<'_>) -> Result { + match value { + BorrowedFormatItem::Component(component) => Ok(component), + _ => Err(error::DifferentVariant), + } + } +} + +impl<'a> From<&'a [BorrowedFormatItem<'_>]> for BorrowedFormatItem<'a> { + fn from(items: &'a [BorrowedFormatItem<'_>]) -> Self { + Self::Compound(items) + } +} + +impl<'a> TryFrom> for &[BorrowedFormatItem<'a>] { + type Error = error::DifferentVariant; + + fn try_from(value: BorrowedFormatItem<'a>) -> Result { + match value { + BorrowedFormatItem::Compound(items) => Ok(items), + _ => Err(error::DifferentVariant), + } + } +} + +impl PartialEq for BorrowedFormatItem<'_> { + fn eq(&self, rhs: &Component) -> bool { + matches!(self, Self::Component(component) if component == rhs) + } +} + +impl PartialEq> for Component { + fn eq(&self, rhs: &BorrowedFormatItem<'_>) -> bool { + rhs == self + } +} + +impl PartialEq<&[Self]> for BorrowedFormatItem<'_> { + fn eq(&self, rhs: &&[Self]) -> bool { + matches!(self, Self::Compound(compound) if compound == rhs) + } +} + +impl PartialEq> for &[BorrowedFormatItem<'_>] { + fn eq(&self, rhs: &BorrowedFormatItem<'_>) -> bool { + rhs == self + } +} diff --git a/time/src/format_description/mod.rs b/time/src/format_description/mod.rs index eb0f3cf1b..261cf348f 100644 --- a/time/src/format_description/mod.rs +++ b/time/src/format_description/mod.rs @@ -5,20 +5,17 @@ //! [`format_description!`](crate::macros::format_description) macro, the //! [`format_description::parse`](crate::format_description::parse()) function. +mod borrowed_format_item; mod component; pub mod modifier; #[cfg(feature = "alloc")] mod parse; -#[cfg(feature = "alloc")] -use alloc::string::String; -#[cfg(feature = "alloc")] -use core::fmt; +pub use borrowed_format_item::BorrowedFormatItem as FormatItem; pub use self::component::Component; #[cfg(feature = "alloc")] pub use self::parse::parse; -use crate::error; /// Well-known formats, typically standards. pub mod well_known { @@ -31,100 +28,3 @@ pub mod well_known { pub use rfc2822::Rfc2822; pub use rfc3339::Rfc3339; } - -/// A complete description of how to format and parse a type. -#[non_exhaustive] -#[cfg_attr(not(feature = "alloc"), derive(Debug))] -#[derive(Clone, PartialEq, Eq)] -pub enum FormatItem<'a> { - /// 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(&'a [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(&'a [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(&'a Self), - /// A series of `FormatItem`s where, when parsing, the first successful parse is used. When - /// formatting, the first element of the slice is used. An empty slice is a no-op when - /// formatting or parsing. - First(&'a [Self]), -} - -#[cfg(feature = "alloc")] -impl fmt::Debug for FormatItem<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - FormatItem::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)), - FormatItem::Component(component) => component.fmt(f), - FormatItem::Compound(compound) => compound.fmt(f), - FormatItem::Optional(item) => f.debug_tuple("Optional").field(item).finish(), - FormatItem::First(items) => f.debug_tuple("First").field(items).finish(), - } - } -} - -impl From for FormatItem<'_> { - fn from(component: Component) -> Self { - Self::Component(component) - } -} - -impl TryFrom> for Component { - type Error = error::DifferentVariant; - - fn try_from(value: FormatItem<'_>) -> Result { - match value { - FormatItem::Component(component) => Ok(component), - _ => Err(error::DifferentVariant), - } - } -} - -impl<'a> From<&'a [FormatItem<'_>]> for FormatItem<'a> { - fn from(items: &'a [FormatItem<'_>]) -> FormatItem<'a> { - FormatItem::Compound(items) - } -} - -impl<'a> TryFrom> for &[FormatItem<'a>] { - type Error = error::DifferentVariant; - - fn try_from(value: FormatItem<'a>) -> Result { - match value { - FormatItem::Compound(items) => Ok(items), - _ => Err(error::DifferentVariant), - } - } -} - -impl PartialEq for FormatItem<'_> { - fn eq(&self, rhs: &Component) -> bool { - matches!(self, FormatItem::Component(component) if component == rhs) - } -} - -impl PartialEq> for Component { - fn eq(&self, rhs: &FormatItem<'_>) -> bool { - rhs == self - } -} - -impl PartialEq<&[FormatItem<'_>]> for FormatItem<'_> { - fn eq(&self, rhs: &&[FormatItem<'_>]) -> bool { - matches!(self, FormatItem::Compound(compound) if compound == rhs) - } -} - -impl PartialEq> for &[FormatItem<'_>] { - fn eq(&self, rhs: &FormatItem<'_>) -> bool { - rhs == self - } -}