summaryrefslogtreecommitdiffstats
path: root/vendor/time/src/format_description
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/time/src/format_description')
-rw-r--r--vendor/time/src/format_description/borrowed_format_item.rs106
-rw-r--r--vendor/time/src/format_description/component.rs37
-rw-r--r--vendor/time/src/format_description/mod.rs34
-rw-r--r--vendor/time/src/format_description/modifier.rs355
-rw-r--r--vendor/time/src/format_description/owned_format_item.rs162
-rw-r--r--vendor/time/src/format_description/parse/ast.rs278
-rw-r--r--vendor/time/src/format_description/parse/format_item.rs386
-rw-r--r--vendor/time/src/format_description/parse/lexer.rs159
-rw-r--r--vendor/time/src/format_description/parse/mod.rs193
-rw-r--r--vendor/time/src/format_description/well_known/iso8601.rs233
-rw-r--r--vendor/time/src/format_description/well_known/iso8601/adt_hack.rs249
-rw-r--r--vendor/time/src/format_description/well_known/rfc2822.rs30
-rw-r--r--vendor/time/src/format_description/well_known/rfc3339.rs30
13 files changed, 2252 insertions, 0 deletions
diff --git a/vendor/time/src/format_description/borrowed_format_item.rs b/vendor/time/src/format_description/borrowed_format_item.rs
new file mode 100644
index 000000000..425b90287
--- /dev/null
+++ b/vendor/time/src/format_description/borrowed_format_item.rs
@@ -0,0 +1,106 @@
+//! A format item with borrowed data.
+
+#[cfg(feature = "alloc")]
+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 {
+ 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(),
+ }
+ }
+}
+
+impl From<Component> for BorrowedFormatItem<'_> {
+ fn from(component: Component) -> Self {
+ Self::Component(component)
+ }
+}
+
+impl TryFrom<BorrowedFormatItem<'_>> for Component {
+ type Error = error::DifferentVariant;
+
+ fn try_from(value: BorrowedFormatItem<'_>) -> Result<Self, Self::Error> {
+ 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<BorrowedFormatItem<'a>> for &[BorrowedFormatItem<'a>] {
+ type Error = error::DifferentVariant;
+
+ fn try_from(value: BorrowedFormatItem<'a>) -> Result<Self, Self::Error> {
+ match value {
+ BorrowedFormatItem::Compound(items) => Ok(items),
+ _ => Err(error::DifferentVariant),
+ }
+ }
+}
+
+impl PartialEq<Component> for BorrowedFormatItem<'_> {
+ fn eq(&self, rhs: &Component) -> bool {
+ matches!(self, Self::Component(component) if component == rhs)
+ }
+}
+
+impl PartialEq<BorrowedFormatItem<'_>> 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<BorrowedFormatItem<'_>> for &[BorrowedFormatItem<'_>] {
+ fn eq(&self, rhs: &BorrowedFormatItem<'_>) -> bool {
+ rhs == self
+ }
+}
diff --git a/vendor/time/src/format_description/component.rs b/vendor/time/src/format_description/component.rs
new file mode 100644
index 000000000..68d162e26
--- /dev/null
+++ b/vendor/time/src/format_description/component.rs
@@ -0,0 +1,37 @@
+//! Part of a format description.
+
+use crate::format_description::modifier;
+
+/// A component of a larger format description.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Component {
+ /// Day of the month.
+ Day(modifier::Day),
+ /// Month of the year.
+ Month(modifier::Month),
+ /// Ordinal day of the year.
+ Ordinal(modifier::Ordinal),
+ /// Day of the week.
+ Weekday(modifier::Weekday),
+ /// Week within the year.
+ WeekNumber(modifier::WeekNumber),
+ /// Year of the date.
+ Year(modifier::Year),
+ /// Hour of the day.
+ Hour(modifier::Hour),
+ /// Minute within the hour.
+ Minute(modifier::Minute),
+ /// AM/PM part of the time.
+ Period(modifier::Period),
+ /// Second within the minute.
+ Second(modifier::Second),
+ /// Subsecond within the second.
+ Subsecond(modifier::Subsecond),
+ /// Hour of the UTC offset.
+ OffsetHour(modifier::OffsetHour),
+ /// Minute within the hour of the UTC offset.
+ OffsetMinute(modifier::OffsetMinute),
+ /// Second within the minute of the UTC offset.
+ OffsetSecond(modifier::OffsetSecond),
+}
diff --git a/vendor/time/src/format_description/mod.rs b/vendor/time/src/format_description/mod.rs
new file mode 100644
index 000000000..7712288e7
--- /dev/null
+++ b/vendor/time/src/format_description/mod.rs
@@ -0,0 +1,34 @@
+//! Description of how types should be formatted and parsed.
+//!
+//! The formatted value will be output to the provided writer. Format descriptions can be
+//! [well-known](crate::format_description::well_known) or obtained by using the
+//! [`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 owned_format_item;
+#[cfg(feature = "alloc")]
+mod parse;
+
+pub use borrowed_format_item::BorrowedFormatItem as FormatItem;
+#[cfg(feature = "alloc")]
+pub use owned_format_item::OwnedFormatItem;
+
+pub use self::component::Component;
+#[cfg(feature = "alloc")]
+pub use self::parse::{parse, parse_owned};
+
+/// Well-known formats, typically standards.
+pub mod well_known {
+ pub mod iso8601;
+ mod rfc2822;
+ mod rfc3339;
+
+ #[doc(inline)]
+ pub use iso8601::Iso8601;
+ pub use rfc2822::Rfc2822;
+ pub use rfc3339::Rfc3339;
+}
diff --git a/vendor/time/src/format_description/modifier.rs b/vendor/time/src/format_description/modifier.rs
new file mode 100644
index 000000000..e01c18fda
--- /dev/null
+++ b/vendor/time/src/format_description/modifier.rs
@@ -0,0 +1,355 @@
+//! Various modifiers for components.
+
+// region: date modifiers
+/// Day of the month.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Day {
+ /// The padding to obtain the minimum width.
+ pub padding: Padding,
+}
+
+/// The representation of a month.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum MonthRepr {
+ /// The number of the month (January is 1, December is 12).
+ Numerical,
+ /// The long form of the month name (e.g. "January").
+ Long,
+ /// The short form of the month name (e.g. "Jan").
+ Short,
+}
+
+/// Month of the year.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Month {
+ /// The padding to obtain the minimum width.
+ pub padding: Padding,
+ /// What form of representation should be used?
+ pub repr: MonthRepr,
+ /// Is the value case sensitive when parsing?
+ pub case_sensitive: bool,
+}
+
+/// Ordinal day of the year.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Ordinal {
+ /// The padding to obtain the minimum width.
+ pub padding: Padding,
+}
+
+/// The representation used for the day of the week.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum WeekdayRepr {
+ /// The short form of the weekday (e.g. "Mon").
+ Short,
+ /// The long form of the weekday (e.g. "Monday").
+ Long,
+ /// A numerical representation using Sunday as the first day of the week.
+ ///
+ /// Sunday is either 0 or 1, depending on the other modifier's value.
+ Sunday,
+ /// A numerical representation using Monday as the first day of the week.
+ ///
+ /// Monday is either 0 or 1, depending on the other modifier's value.
+ Monday,
+}
+
+/// Day of the week.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Weekday {
+ /// What form of representation should be used?
+ pub repr: WeekdayRepr,
+ /// When using a numerical representation, should it be zero or one-indexed?
+ pub one_indexed: bool,
+ /// Is the value case sensitive when parsing?
+ pub case_sensitive: bool,
+}
+
+/// The representation used for the week number.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum WeekNumberRepr {
+ /// Week 1 is the week that contains January 4.
+ Iso,
+ /// Week 1 begins on the first Sunday of the calendar year.
+ Sunday,
+ /// Week 1 begins on the first Monday of the calendar year.
+ Monday,
+}
+
+/// Week within the year.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct WeekNumber {
+ /// The padding to obtain the minimum width.
+ pub padding: Padding,
+ /// What kind of representation should be used?
+ pub repr: WeekNumberRepr,
+}
+
+/// The representation used for a year value.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum YearRepr {
+ /// The full value of the year.
+ Full,
+ /// Only the last two digits of the year.
+ LastTwo,
+}
+
+/// Year of the date.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Year {
+ /// The padding to obtain the minimum width.
+ pub padding: Padding,
+ /// What kind of representation should be used?
+ pub repr: YearRepr,
+ /// Whether the value is based on the ISO week number or the Gregorian calendar.
+ pub iso_week_based: bool,
+ /// Whether the `+` sign is present when a positive year contains fewer than five digits.
+ pub sign_is_mandatory: bool,
+}
+// endregion date modifiers
+
+// region: time modifiers
+/// Hour of the day.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Hour {
+ /// The padding to obtain the minimum width.
+ pub padding: Padding,
+ /// Is the hour displayed using a 12 or 24-hour clock?
+ pub is_12_hour_clock: bool,
+}
+
+/// Minute within the hour.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Minute {
+ /// The padding to obtain the minimum width.
+ pub padding: Padding,
+}
+
+/// AM/PM part of the time.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Period {
+ /// Is the period uppercase or lowercase?
+ pub is_uppercase: bool,
+ /// Is the value case sensitive when parsing?
+ ///
+ /// Note that when `false`, the `is_uppercase` field has no effect on parsing behavior.
+ pub case_sensitive: bool,
+}
+
+/// Second within the minute.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Second {
+ /// The padding to obtain the minimum width.
+ pub padding: Padding,
+}
+
+/// The number of digits present in a subsecond representation.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum SubsecondDigits {
+ /// Exactly one digit.
+ One,
+ /// Exactly two digits.
+ Two,
+ /// Exactly three digits.
+ Three,
+ /// Exactly four digits.
+ Four,
+ /// Exactly five digits.
+ Five,
+ /// Exactly six digits.
+ Six,
+ /// Exactly seven digits.
+ Seven,
+ /// Exactly eight digits.
+ Eight,
+ /// Exactly nine digits.
+ Nine,
+ /// Any number of digits (up to nine) that is at least one. When formatting, the minimum digits
+ /// necessary will be used.
+ OneOrMore,
+}
+
+/// Subsecond within the second.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Subsecond {
+ /// How many digits are present in the component?
+ pub digits: SubsecondDigits,
+}
+// endregion time modifiers
+
+// region: offset modifiers
+/// Hour of the UTC offset.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct OffsetHour {
+ /// Whether the `+` sign is present on positive values.
+ pub sign_is_mandatory: bool,
+ /// The padding to obtain the minimum width.
+ pub padding: Padding,
+}
+
+/// Minute within the hour of the UTC offset.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct OffsetMinute {
+ /// The padding to obtain the minimum width.
+ pub padding: Padding,
+}
+
+/// Second within the minute of the UTC offset.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct OffsetSecond {
+ /// The padding to obtain the minimum width.
+ pub padding: Padding,
+}
+// endregion offset modifiers
+
+/// Type of padding to ensure a minimum width.
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Padding {
+ /// A space character (` `) should be used as padding.
+ Space,
+ /// A zero character (`0`) should be used as padding.
+ Zero,
+ /// There is no padding. This can result in a width below the otherwise minimum number of
+ /// characters.
+ None,
+}
+
+/// Generate the provided code if and only if `pub` is present.
+macro_rules! if_pub {
+ (pub $(#[$attr:meta])*; $($x:tt)*) => {
+ $(#[$attr])*
+ ///
+ /// This function exists since [`Default::default()`] cannot be used in a `const` context.
+ /// It may be removed once that becomes possible. As the [`Default`] trait is in the
+ /// prelude, removing this function in the future will not cause any resolution failures for
+ /// the overwhelming majority of users; only users who use `#![no_implicit_prelude]` will be
+ /// affected. As such it will not be considered a breaking change.
+ $($x)*
+ };
+ ($($_:tt)*) => {};
+}
+
+/// Implement `Default` for the given type. This also generates an inherent implementation of a
+/// `default` method that is `const fn`, permitting the default value to be used in const contexts.
+// Every modifier should use this macro rather than a derived `Default`.
+macro_rules! impl_const_default {
+ ($($(#[$doc:meta])* $(@$pub:ident)? $type:ty => $default:expr;)*) => {$(
+ impl $type {
+ if_pub! {
+ $($pub)?
+ $(#[$doc])*;
+ pub const fn default() -> Self {
+ $default
+ }
+ }
+ }
+
+ $(#[$doc])*
+ impl Default for $type {
+ fn default() -> Self {
+ $default
+ }
+ }
+ )*};
+}
+
+impl_const_default! {
+ /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
+ @pub Day => Self { padding: Padding::Zero };
+ /// Creates a modifier that indicates the value uses the
+ /// [`Numerical`](Self::Numerical) representation.
+ MonthRepr => Self::Numerical;
+ /// Creates an instance of this type that indicates the value uses the
+ /// [`Numerical`](MonthRepr::Numerical) representation, is [padded with zeroes](Padding::Zero),
+ /// and is case-sensitive when parsing.
+ @pub Month => Self {
+ padding: Padding::Zero,
+ repr: MonthRepr::Numerical,
+ case_sensitive: true,
+ };
+ /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
+ @pub Ordinal => Self { padding: Padding::Zero };
+ /// Creates a modifier that indicates the value uses the [`Long`](Self::Long) representation.
+ WeekdayRepr => Self::Long;
+ /// Creates a modifier that indicates the value uses the [`Long`](WeekdayRepr::Long)
+ /// representation and is case-sensitive when parsing. If the representation is changed to a
+ /// numerical one, the instance defaults to one-based indexing.
+ @pub Weekday => Self {
+ repr: WeekdayRepr::Long,
+ one_indexed: true,
+ case_sensitive: true,
+ };
+ /// Creates a modifier that indicates that the value uses the [`Iso`](Self::Iso) representation.
+ WeekNumberRepr => Self::Iso;
+ /// Creates a modifier that indicates that the value is [padded with zeroes](Padding::Zero)
+ /// and uses the [`Iso`](WeekNumberRepr::Iso) representation.
+ @pub WeekNumber => Self {
+ padding: Padding::Zero,
+ repr: WeekNumberRepr::Iso,
+ };
+ /// Creates a modifier that indicates the value uses the [`Full`](Self::Full) representation.
+ YearRepr => Self::Full;
+ /// Creates a modifier that indicates the value uses the [`Full`](YearRepr::Full)
+ /// representation, is [padded with zeroes](Padding::Zero), uses the Gregorian calendar as its
+ /// base, and only includes the year's sign if necessary.
+ @pub Year => Self {
+ padding: Padding::Zero,
+ repr: YearRepr::Full,
+ iso_week_based: false,
+ sign_is_mandatory: false,
+ };
+ /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero) and
+ /// has the 24-hour representation.
+ @pub Hour => Self {
+ padding: Padding::Zero,
+ is_12_hour_clock: false,
+ };
+ /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
+ @pub Minute => Self { padding: Padding::Zero };
+ /// Creates a modifier that indicates the value uses the upper-case representation and is
+ /// case-sensitive when parsing.
+ @pub Period => Self {
+ is_uppercase: true,
+ case_sensitive: true,
+ };
+ /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
+ @pub Second => Self { padding: Padding::Zero };
+ /// Creates a modifier that indicates the stringified value contains [one or more
+ /// digits](Self::OneOrMore).
+ SubsecondDigits => Self::OneOrMore;
+ /// Creates a modifier that indicates the stringified value contains [one or more
+ /// digits](SubsecondDigits::OneOrMore).
+ @pub Subsecond => Self { digits: SubsecondDigits::OneOrMore };
+ /// Creates a modifier that indicates the value uses the `+` sign for all positive values
+ /// and is [padded with zeroes](Padding::Zero).
+ @pub OffsetHour => Self {
+ sign_is_mandatory: true,
+ padding: Padding::Zero,
+ };
+ /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
+ @pub OffsetMinute => Self { padding: Padding::Zero };
+ /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
+ @pub OffsetSecond => Self { padding: Padding::Zero };
+ /// Creates a modifier that indicates the value is [padded with zeroes](Self::Zero).
+ Padding => Self::Zero;
+}
diff --git a/vendor/time/src/format_description/owned_format_item.rs b/vendor/time/src/format_description/owned_format_item.rs
new file mode 100644
index 000000000..6f7f7c233
--- /dev/null
+++ b/vendor/time/src/format_description/owned_format_item.rs
@@ -0,0 +1,162 @@
+//! 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<Self>),
+ /// 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<FormatItem<'_>> 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::<Vec<_>>()
+ .into_boxed_slice(),
+ ),
+ FormatItem::Optional(item) => Self::Optional(Box::new((*item).into())),
+ FormatItem::First(items) => Self::First(
+ items
+ .iter()
+ .cloned()
+ .map(Into::into)
+ .collect::<Vec<_>>()
+ .into_boxed_slice(),
+ ),
+ }
+ }
+}
+
+impl From<Vec<FormatItem<'_>>> for OwnedFormatItem {
+ fn from(items: Vec<FormatItem<'_>>) -> 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::<Vec<_>>()
+ .into_boxed_slice(),
+ )
+ }
+}
+// endregion conversions from FormatItem
+
+// region: from variants
+impl From<Component> for OwnedFormatItem {
+ fn from(component: Component) -> Self {
+ Self::Component(component)
+ }
+}
+
+impl TryFrom<OwnedFormatItem> for Component {
+ type Error = error::DifferentVariant;
+
+ fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> {
+ match value {
+ OwnedFormatItem::Component(component) => Ok(component),
+ _ => Err(error::DifferentVariant),
+ }
+ }
+}
+
+impl From<Vec<Self>> for OwnedFormatItem {
+ fn from(items: Vec<Self>) -> Self {
+ Self::Compound(items.into_boxed_slice())
+ }
+}
+
+impl TryFrom<OwnedFormatItem> for Vec<OwnedFormatItem> {
+ type Error = error::DifferentVariant;
+
+ fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> {
+ match value {
+ OwnedFormatItem::Compound(items) => Ok(items.into_vec()),
+ _ => Err(error::DifferentVariant),
+ }
+ }
+}
+// endregion from variants
+
+// region: equality
+impl PartialEq<Component> for OwnedFormatItem {
+ fn eq(&self, rhs: &Component) -> bool {
+ matches!(self, Self::Component(component) if component == rhs)
+ }
+}
+
+impl PartialEq<OwnedFormatItem> 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<OwnedFormatItem> for &[OwnedFormatItem] {
+ fn eq(&self, rhs: &OwnedFormatItem) -> bool {
+ rhs == self
+ }
+}
+// endregion equality
diff --git a/vendor/time/src/format_description/parse/ast.rs b/vendor/time/src/format_description/parse/ast.rs
new file mode 100644
index 000000000..6977cc9cd
--- /dev/null
+++ b/vendor/time/src/format_description/parse/ast.rs
@@ -0,0 +1,278 @@
+//! AST for parsing format descriptions.
+
+use alloc::string::String;
+use alloc::vec::Vec;
+use core::iter;
+use core::iter::Peekable;
+
+use super::{lexer, Error, Location, Span};
+
+/// One part of a complete format description.
+#[allow(variant_size_differences)]
+pub(super) enum Item<'a> {
+ /// A literal string, formatted and parsed as-is.
+ Literal {
+ /// The string itself.
+ value: &'a [u8],
+ /// Where the string originates from in the format string.
+ _span: Span,
+ },
+ /// A sequence of brackets. The first acts as the escape character.
+ EscapedBracket {
+ /// The first bracket.
+ _first: Location,
+ /// The second bracket.
+ _second: Location,
+ },
+ /// Part of a type, along with its modifiers.
+ Component {
+ /// Where the opening bracket was in the format string.
+ _opening_bracket: Location,
+ /// Whitespace between the opening bracket and name.
+ _leading_whitespace: Option<Whitespace<'a>>,
+ /// The name of the component.
+ name: Name<'a>,
+ /// The modifiers for the component.
+ modifiers: Vec<Modifier<'a>>,
+ /// Whitespace between the modifiers and closing bracket.
+ _trailing_whitespace: Option<Whitespace<'a>>,
+ /// Where the closing bracket was in the format string.
+ _closing_bracket: Location,
+ },
+}
+
+/// Whitespace within a component.
+pub(super) struct Whitespace<'a> {
+ /// The whitespace itself.
+ pub(super) _value: &'a [u8],
+ /// Where the whitespace was in the format string.
+ pub(super) span: Span,
+}
+
+/// The name of a component.
+pub(super) struct Name<'a> {
+ /// The name itself.
+ pub(super) value: &'a [u8],
+ /// Where the name was in the format string.
+ pub(super) span: Span,
+}
+
+/// A modifier for a component.
+pub(super) struct Modifier<'a> {
+ /// Whitespace preceding the modifier.
+ pub(super) _leading_whitespace: Whitespace<'a>,
+ /// The key of the modifier.
+ pub(super) key: Key<'a>,
+ /// Where the colon of the modifier was in the format string.
+ pub(super) _colon: Location,
+ /// The value of the modifier.
+ pub(super) value: Value<'a>,
+}
+
+/// The key of a modifier.
+pub(super) struct Key<'a> {
+ /// The key itself.
+ pub(super) value: &'a [u8],
+ /// Where the key was in the format string.
+ pub(super) span: Span,
+}
+
+/// The value of a modifier.
+pub(super) struct Value<'a> {
+ /// The value itself.
+ pub(super) value: &'a [u8],
+ /// Where the value was in the format string.
+ pub(super) span: Span,
+}
+
+/// Parse the provided tokens into an AST.
+pub(super) fn parse<'a>(
+ tokens: impl Iterator<Item = lexer::Token<'a>>,
+) -> impl Iterator<Item = Result<Item<'a>, Error>> {
+ let mut tokens = tokens.peekable();
+ iter::from_fn(move || {
+ Some(match tokens.next()? {
+ lexer::Token::Literal { value, span } => Ok(Item::Literal { value, _span: span }),
+ lexer::Token::Bracket {
+ kind: lexer::BracketKind::Opening,
+ location,
+ } => {
+ // escaped bracket
+ if let Some(&lexer::Token::Bracket {
+ kind: lexer::BracketKind::Opening,
+ location: second_location,
+ }) = tokens.peek()
+ {
+ tokens.next(); // consume
+ Ok(Item::EscapedBracket {
+ _first: location,
+ _second: second_location,
+ })
+ }
+ // component
+ else {
+ parse_component(location, &mut tokens)
+ }
+ }
+ lexer::Token::Bracket {
+ kind: lexer::BracketKind::Closing,
+ location: _,
+ } => unreachable!(
+ "internal error: closing bracket should have been consumed by `parse_component`",
+ ),
+ lexer::Token::ComponentPart {
+ kind: _,
+ value: _,
+ span: _,
+ } => unreachable!(
+ "internal error: component part should have been consumed by `parse_component`",
+ ),
+ })
+ })
+}
+
+/// Parse a component. This assumes that the opening bracket has already been consumed.
+fn parse_component<'a>(
+ opening_bracket: Location,
+ tokens: &mut Peekable<impl Iterator<Item = lexer::Token<'a>>>,
+) -> Result<Item<'a>, Error> {
+ let leading_whitespace = if let Some(&lexer::Token::ComponentPart {
+ kind: lexer::ComponentKind::Whitespace,
+ value,
+ span,
+ }) = tokens.peek()
+ {
+ tokens.next(); // consume
+ Some(Whitespace {
+ _value: value,
+ span,
+ })
+ } else {
+ None
+ };
+
+ let name = if let Some(&lexer::Token::ComponentPart {
+ kind: lexer::ComponentKind::NotWhitespace,
+ value,
+ span,
+ }) = tokens.peek()
+ {
+ tokens.next(); // consume
+ Name { value, span }
+ } else {
+ let span = leading_whitespace.map_or_else(
+ || Span {
+ start: opening_bracket,
+ end: opening_bracket,
+ },
+ |whitespace| whitespace.span.shrink_to_end(),
+ );
+ return Err(Error {
+ _inner: span.error("expected component name"),
+ public: crate::error::InvalidFormatDescription::MissingComponentName {
+ index: span.start_byte(),
+ },
+ });
+ };
+
+ let mut modifiers = Vec::new();
+ let trailing_whitespace = loop {
+ let whitespace = if let Some(&lexer::Token::ComponentPart {
+ kind: lexer::ComponentKind::Whitespace,
+ value,
+ span,
+ }) = tokens.peek()
+ {
+ tokens.next(); // consume
+ Whitespace {
+ _value: value,
+ span,
+ }
+ } else {
+ break None;
+ };
+
+ if let Some(&lexer::Token::ComponentPart {
+ kind: lexer::ComponentKind::NotWhitespace,
+ value,
+ span,
+ }) = tokens.peek()
+ {
+ tokens.next(); // consume
+
+ let colon_index = match value.iter().position(|&b| b == b':') {
+ Some(index) => index,
+ None => {
+ return Err(Error {
+ _inner: span.error("modifier must be of the form `key:value`"),
+ public: crate::error::InvalidFormatDescription::InvalidModifier {
+ value: String::from_utf8_lossy(value).into_owned(),
+ index: span.start_byte(),
+ },
+ });
+ }
+ };
+ let key = &value[..colon_index];
+ let value = &value[colon_index + 1..];
+
+ if key.is_empty() {
+ return Err(Error {
+ _inner: span.shrink_to_start().error("expected modifier key"),
+ public: crate::error::InvalidFormatDescription::InvalidModifier {
+ value: String::new(),
+ index: span.start_byte(),
+ },
+ });
+ }
+ if value.is_empty() {
+ return Err(Error {
+ _inner: span.shrink_to_end().error("expected modifier value"),
+ public: crate::error::InvalidFormatDescription::InvalidModifier {
+ value: String::new(),
+ index: span.shrink_to_end().start_byte(),
+ },
+ });
+ }
+
+ modifiers.push(Modifier {
+ _leading_whitespace: whitespace,
+ key: Key {
+ value: key,
+ span: span.subspan(..colon_index),
+ },
+ _colon: span.start.offset(colon_index),
+ value: Value {
+ value,
+ span: span.subspan(colon_index + 1..),
+ },
+ });
+ } else {
+ break Some(whitespace);
+ }
+ };
+
+ let closing_bracket = if let Some(&lexer::Token::Bracket {
+ kind: lexer::BracketKind::Closing,
+ location,
+ }) = tokens.peek()
+ {
+ tokens.next(); // consume
+ location
+ } else {
+ return Err(Error {
+ _inner: opening_bracket.error("unclosed bracket"),
+ public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket {
+ index: opening_bracket.byte,
+ },
+ });
+ };
+
+ Ok(Item::Component {
+ _opening_bracket: opening_bracket,
+ _leading_whitespace: leading_whitespace,
+ name,
+ modifiers,
+ _trailing_whitespace: trailing_whitespace,
+ _closing_bracket: closing_bracket,
+ })
+}
diff --git a/vendor/time/src/format_description/parse/format_item.rs b/vendor/time/src/format_description/parse/format_item.rs
new file mode 100644
index 000000000..53146d522
--- /dev/null
+++ b/vendor/time/src/format_description/parse/format_item.rs
@@ -0,0 +1,386 @@
+//! Typed, validated representation of a parsed format description.
+
+use alloc::string::String;
+
+use super::{ast, Error};
+
+/// Parse an AST iterator into a sequence of format items.
+pub(super) fn parse<'a>(
+ ast_items: impl Iterator<Item = Result<ast::Item<'a>, Error>>,
+) -> impl Iterator<Item = Result<Item<'a>, Error>> {
+ ast_items.map(|ast_item| ast_item.and_then(Item::from_ast))
+}
+
+/// A description of how to format and parse one part of a type.
+#[allow(variant_size_differences)]
+pub(super) enum Item<'a> {
+ /// A literal string.
+ Literal(&'a [u8]),
+ /// Part of a type, along with its modifiers.
+ Component(Component),
+}
+
+impl Item<'_> {
+ /// Parse an AST item into a format item.
+ pub(super) fn from_ast(ast_item: ast::Item<'_>) -> Result<Item<'_>, Error> {
+ Ok(match ast_item {
+ ast::Item::Component {
+ _opening_bracket: _,
+ _leading_whitespace: _,
+ name,
+ modifiers,
+ _trailing_whitespace: _,
+ _closing_bracket: _,
+ } => Item::Component(component_from_ast(&name, &modifiers)?),
+ ast::Item::Literal { value, _span: _ } => Item::Literal(value),
+ ast::Item::EscapedBracket {
+ _first: _,
+ _second: _,
+ } => Item::Literal(b"["),
+ })
+ }
+}
+
+impl<'a> From<Item<'a>> for crate::format_description::FormatItem<'a> {
+ fn from(item: Item<'a>) -> Self {
+ match item {
+ Item::Literal(literal) => Self::Literal(literal),
+ Item::Component(component) => Self::Component(component.into()),
+ }
+ }
+}
+
+impl From<Item<'_>> for crate::format_description::OwnedFormatItem {
+ fn from(item: Item<'_>) -> Self {
+ match item {
+ Item::Literal(literal) => Self::Literal(literal.to_vec().into_boxed_slice()),
+ Item::Component(component) => Self::Component(component.into()),
+ }
+ }
+}
+
+/// Declare the `Component` struct.
+macro_rules! component_definition {
+ ($vis:vis enum $name:ident {
+ $($variant:ident = $parse_variant:literal {
+ $($field:ident = $parse_field:literal:
+ Option<$field_type:ty> => $target_field:ident),* $(,)?
+ }),* $(,)?
+ }) => {
+ $vis enum $name {
+ $($variant($variant),)*
+ }
+
+ $($vis struct $variant {
+ $($field: Option<$field_type>),*
+ })*
+
+ $(impl $variant {
+ /// Parse the component from the AST, given its modifiers.
+ fn with_modifiers(modifiers: &[ast::Modifier<'_>]) -> Result<Self, Error> {
+ let mut this = Self {
+ $($field: None),*
+ };
+
+ for modifier in modifiers {
+ $(if modifier.key.value.eq_ignore_ascii_case($parse_field) {
+ this.$field = <$field_type>::from_modifier_value(&modifier.value)?;
+ continue;
+ })*
+ return Err(Error {
+ _inner: modifier.key.span.error("invalid modifier key"),
+ public: crate::error::InvalidFormatDescription::InvalidModifier {
+ value: String::from_utf8_lossy(modifier.key.value).into_owned(),
+ index: modifier.key.span.start_byte(),
+ }
+ });
+ }
+
+ Ok(this)
+ }
+ })*
+
+ impl From<$name> for crate::format_description::Component {
+ fn from(component: $name) -> Self {
+ match component {$(
+ $name::$variant($variant { $($field),* }) => {
+ $crate::format_description::component::Component::$variant(
+ $crate::format_description::modifier::$variant {$(
+ $target_field: $field.unwrap_or_default().into()
+ ),*}
+ )
+ }
+ )*}
+ }
+ }
+
+ /// Parse a component from the AST, given its name and modifiers.
+ fn component_from_ast(
+ name: &ast::Name<'_>,
+ modifiers: &[ast::Modifier<'_>],
+ ) -> Result<Component, Error> {
+ $(if name.value.eq_ignore_ascii_case($parse_variant) {
+ return Ok(Component::$variant($variant::with_modifiers(&modifiers)?));
+ })*
+ Err(Error {
+ _inner: name.span.error("invalid component"),
+ public: crate::error::InvalidFormatDescription::InvalidComponentName {
+ name: String::from_utf8_lossy(name.value).into_owned(),
+ index: name.span.start_byte(),
+ },
+ })
+ }
+ }
+}
+
+// Keep in alphabetical order.
+component_definition! {
+ pub(super) enum Component {
+ Day = b"day" {
+ padding = b"padding": Option<Padding> => padding,
+ },
+ Hour = b"hour" {
+ padding = b"padding": Option<Padding> => padding,
+ base = b"repr": Option<HourBase> => is_12_hour_clock,
+ },
+ Minute = b"minute" {
+ padding = b"padding": Option<Padding> => padding,
+ },
+ Month = b"month" {
+ padding = b"padding": Option<Padding> => padding,
+ repr = b"repr": Option<MonthRepr> => repr,
+ case_sensitive = b"case_sensitive": Option<MonthCaseSensitive> => case_sensitive,
+ },
+ OffsetHour = b"offset_hour" {
+ sign_behavior = b"sign": Option<SignBehavior> => sign_is_mandatory,
+ padding = b"padding": Option<Padding> => padding,
+ },
+ OffsetMinute = b"offset_minute" {
+ padding = b"padding": Option<Padding> => padding,
+ },
+ OffsetSecond = b"offset_second" {
+ padding = b"padding": Option<Padding> => padding,
+ },
+ Ordinal = b"ordinal" {
+ padding = b"padding": Option<Padding> => padding,
+ },
+ Period = b"period" {
+ case = b"case": Option<PeriodCase> => is_uppercase,
+ case_sensitive = b"case_sensitive": Option<PeriodCaseSensitive> => case_sensitive,
+ },
+ Second = b"second" {
+ padding = b"padding": Option<Padding> => padding,
+ },
+ Subsecond = b"subsecond" {
+ digits = b"digits": Option<SubsecondDigits> => digits,
+ },
+ Weekday = b"weekday" {
+ repr = b"repr": Option<WeekdayRepr> => repr,
+ one_indexed = b"one_indexed": Option<WeekdayOneIndexed> => one_indexed,
+ case_sensitive = b"case_sensitive": Option<WeekdayCaseSensitive> => case_sensitive,
+ },
+ WeekNumber = b"week_number" {
+ padding = b"padding": Option<Padding> => padding,
+ repr = b"repr": Option<WeekNumberRepr> => repr,
+ },
+ Year = b"year" {
+ padding = b"padding": Option<Padding> => padding,
+ repr = b"repr": Option<YearRepr> => repr,
+ base = b"base": Option<YearBase> => iso_week_based,
+ sign_behavior = b"sign": Option<SignBehavior> => sign_is_mandatory,
+ },
+ }
+}
+
+/// Get the target type for a given enum.
+macro_rules! target_ty {
+ ($name:ident $type:ty) => {
+ $type
+ };
+ ($name:ident) => {
+ $crate::format_description::modifier::$name
+ };
+}
+
+/// Get the target value for a given enum.
+macro_rules! target_value {
+ ($name:ident $variant:ident $value:expr) => {
+ $value
+ };
+ ($name:ident $variant:ident) => {
+ $crate::format_description::modifier::$name::$variant
+ };
+}
+
+// TODO use `#[derive(Default)]` on enums once MSRV is 1.62 (NET 2022-12-30)
+/// Simulate `#[derive(Default)]` on enums.
+macro_rules! derived_default_on_enum {
+ ($type:ty; $default:expr) => {};
+ ($attr:meta $type:ty; $default:expr) => {
+ impl Default for $type {
+ fn default() -> Self {
+ $default
+ }
+ }
+ };
+}
+
+/// Declare the various modifiers.
+///
+/// For the general case, ordinary syntax can be used. Note that you _must_ declare a default
+/// variant. The only significant change is that the string representation of the variant must be
+/// provided after the variant name. For example, `Numerical = b"numerical"` declares a variant
+/// named `Numerical` with the string representation `b"numerical"`. This is the value that will be
+/// used when parsing the modifier. The value is not case sensitive.
+///
+/// If the type in the public API does not have the same name as the type in the internal
+/// representation, then the former must be specified in parenthesis after the internal name. For
+/// example, `HourBase(bool)` has an internal name "HourBase", but is represented as a boolean in
+/// the public API.
+///
+/// By default, the internal variant name is assumed to be the same as the public variant name. If
+/// this is not the case, the qualified path to the variant must be specified in parenthesis after
+/// the internal variant name. For example, `Twelve(true)` has an internal variant name "Twelve",
+/// but is represented as `true` in the public API.
+macro_rules! modifier {
+ ($(
+ enum $name:ident $(($target_ty:ty))? {
+ $(
+ $(#[$attr:meta])?
+ $variant:ident $(($target_value:expr))? = $parse_variant:literal
+ ),* $(,)?
+ }
+ )+) => {$(
+ enum $name {
+ $($variant),*
+ }
+
+ $(derived_default_on_enum! {
+ $($attr)? $name; $name::$variant
+ })*
+
+ impl $name {
+ /// Parse the modifier from its string representation.
+ fn from_modifier_value(value: &ast::Value<'_>) -> Result<Option<Self>, Error> {
+ $(if value.value.eq_ignore_ascii_case($parse_variant) {
+ return Ok(Some(Self::$variant));
+ })*
+ Err(Error {
+ _inner: value.span.error("invalid modifier value"),
+ public: crate::error::InvalidFormatDescription::InvalidModifier {
+ value: String::from_utf8_lossy(value.value).into_owned(),
+ index: value.span.start_byte(),
+ },
+ })
+ }
+ }
+
+ impl From<$name> for target_ty!($name $($target_ty)?) {
+ fn from(modifier: $name) -> Self {
+ match modifier {
+ $($name::$variant => target_value!($name $variant $($target_value)?)),*
+ }
+ }
+ }
+ )+};
+}
+
+// Keep in alphabetical order.
+modifier! {
+ enum HourBase(bool) {
+ Twelve(true) = b"12",
+ #[default]
+ TwentyFour(false) = b"24",
+ }
+
+ enum MonthCaseSensitive(bool) {
+ False(false) = b"false",
+ #[default]
+ True(true) = b"true",
+ }
+
+ enum MonthRepr {
+ #[default]
+ Numerical = b"numerical",
+ Long = b"long",
+ Short = b"short",
+ }
+
+ enum Padding {
+ Space = b"space",
+ #[default]
+ Zero = b"zero",
+ None = b"none",
+ }
+
+ enum PeriodCase(bool) {
+ Lower(false) = b"lower",
+ #[default]
+ Upper(true) = b"upper",
+ }
+
+ enum PeriodCaseSensitive(bool) {
+ False(false) = b"false",
+ #[default]
+ True(true) = b"true",
+ }
+
+ enum SignBehavior(bool) {
+ #[default]
+ Automatic(false) = b"automatic",
+ Mandatory(true) = b"mandatory",
+ }
+
+ enum SubsecondDigits {
+ One = b"1",
+ Two = b"2",
+ Three = b"3",
+ Four = b"4",
+ Five = b"5",
+ Six = b"6",
+ Seven = b"7",
+ Eight = b"8",
+ Nine = b"9",
+ #[default]
+ OneOrMore = b"1+",
+ }
+
+ enum WeekNumberRepr {
+ #[default]
+ Iso = b"iso",
+ Sunday = b"sunday",
+ Monday = b"monday",
+ }
+
+ enum WeekdayCaseSensitive(bool) {
+ False(false) = b"false",
+ #[default]
+ True(true) = b"true",
+ }
+
+ enum WeekdayOneIndexed(bool) {
+ False(false) = b"false",
+ #[default]
+ True(true) = b"true",
+ }
+
+ enum WeekdayRepr {
+ Short = b"short",
+ #[default]
+ Long = b"long",
+ Sunday = b"sunday",
+ Monday = b"monday",
+ }
+
+ enum YearBase(bool) {
+ #[default]
+ Calendar(false) = b"calendar",
+ IsoWeek(true) = b"iso_week",
+ }
+
+ enum YearRepr {
+ #[default]
+ Full = b"full",
+ LastTwo = b"last_two",
+ }
+}
diff --git a/vendor/time/src/format_description/parse/lexer.rs b/vendor/time/src/format_description/parse/lexer.rs
new file mode 100644
index 000000000..e405ea8c8
--- /dev/null
+++ b/vendor/time/src/format_description/parse/lexer.rs
@@ -0,0 +1,159 @@
+//! Lexer for parsing format descriptions.
+
+use core::iter;
+
+use super::{Location, Span};
+
+/// A token emitted by the lexer. There is no semantic meaning at this stage.
+pub(super) enum Token<'a> {
+ /// A literal string, formatted and parsed as-is.
+ Literal {
+ /// The string itself.
+ value: &'a [u8],
+ /// Where the string was in the format string.
+ span: Span,
+ },
+ /// An opening or closing bracket. May or may not be the start or end of a component.
+ Bracket {
+ /// Whether the bracket is opening or closing.
+ kind: BracketKind,
+ /// Where the bracket was in the format string.
+ location: Location,
+ },
+ /// One part of a component. This could be its name, a modifier, or whitespace.
+ ComponentPart {
+ /// Whether the part is whitespace or not.
+ kind: ComponentKind,
+ /// The part itself.
+ value: &'a [u8],
+ /// Where the part was in the format string.
+ span: Span,
+ },
+}
+
+/// What type of bracket is present.
+pub(super) enum BracketKind {
+ /// An opening bracket: `[`
+ Opening,
+ /// A closing bracket: `]`
+ Closing,
+}
+
+/// Indicates whether the component is whitespace or not.
+pub(super) enum ComponentKind {
+ #[allow(clippy::missing_docs_in_private_items)]
+ Whitespace,
+ #[allow(clippy::missing_docs_in_private_items)]
+ NotWhitespace,
+}
+
+/// Attach [`Location`] information to each byte in the iterator.
+fn attach_location(iter: impl Iterator<Item = u8>) -> impl Iterator<Item = (u8, Location)> {
+ let mut line = 1;
+ let mut column = 1;
+ let mut byte_pos = 0;
+
+ iter.map(move |byte| {
+ let location = Location {
+ line,
+ column,
+ byte: byte_pos,
+ };
+ column += 1;
+ byte_pos += 1;
+
+ if byte == b'\n' {
+ line += 1;
+ column = 1;
+ }
+
+ (byte, location)
+ })
+}
+
+/// Parse the string into a series of [`Token`]s.
+pub(super) fn lex(mut input: &[u8]) -> impl Iterator<Item = Token<'_>> {
+ let mut depth: u8 = 0;
+ let mut iter = attach_location(input.iter().copied()).peekable();
+ let mut second_bracket_location = None;
+
+ iter::from_fn(move || {
+ // There is a flag set to emit the second half of an escaped bracket pair.
+ if let Some(location) = second_bracket_location.take() {
+ return Some(Token::Bracket {
+ kind: BracketKind::Opening,
+ location,
+ });
+ }
+
+ Some(match iter.next()? {
+ (b'[', location) => {
+ if let Some((_, second_location)) = iter.next_if(|&(byte, _)| byte == b'[') {
+ // escaped bracket
+ second_bracket_location = Some(second_location);
+ input = &input[2..];
+ } else {
+ // opening bracket
+ depth += 1;
+ input = &input[1..];
+ }
+
+ Token::Bracket {
+ kind: BracketKind::Opening,
+ location,
+ }
+ }
+ // closing bracket
+ (b']', location) if depth > 0 => {
+ depth -= 1;
+ input = &input[1..];
+ Token::Bracket {
+ kind: BracketKind::Closing,
+ location,
+ }
+ }
+ // literal
+ (_, start_location) if depth == 0 => {
+ let mut bytes = 1;
+ let mut end_location = start_location;
+
+ while let Some((_, location)) = iter.next_if(|&(byte, _)| byte != b'[') {
+ end_location = location;
+ bytes += 1;
+ }
+
+ let value = &input[..bytes];
+ input = &input[bytes..];
+ Token::Literal {
+ value,
+ span: Span::start_end(start_location, end_location),
+ }
+ }
+ // component part
+ (byte, start_location) => {
+ let mut bytes = 1;
+ let mut end_location = start_location;
+ let is_whitespace = byte.is_ascii_whitespace();
+
+ while let Some((_, location)) = iter.next_if(|&(byte, _)| {
+ byte != b'[' && byte != b']' && is_whitespace == byte.is_ascii_whitespace()
+ }) {
+ end_location = location;
+ bytes += 1;
+ }
+
+ let value = &input[..bytes];
+ input = &input[bytes..];
+ Token::ComponentPart {
+ kind: if is_whitespace {
+ ComponentKind::Whitespace
+ } else {
+ ComponentKind::NotWhitespace
+ },
+ value,
+ span: Span::start_end(start_location, end_location),
+ }
+ }
+ })
+ })
+}
diff --git a/vendor/time/src/format_description/parse/mod.rs b/vendor/time/src/format_description/parse/mod.rs
new file mode 100644
index 000000000..c73a67449
--- /dev/null
+++ b/vendor/time/src/format_description/parse/mod.rs
@@ -0,0 +1,193 @@
+//! Parser for format descriptions.
+
+use alloc::vec::Vec;
+use core::ops::{RangeFrom, RangeTo};
+
+mod ast;
+mod format_item;
+mod lexer;
+
+/// Parse a sequence of items from the format description.
+///
+/// The syntax for the format description can be found in [the
+/// book](https://time-rs.github.io/book/api/format-description.html).
+pub fn parse(
+ s: &str,
+) -> Result<Vec<crate::format_description::FormatItem<'_>>, crate::error::InvalidFormatDescription>
+{
+ let lexed = lexer::lex(s.as_bytes());
+ let ast = ast::parse(lexed);
+ let format_items = format_item::parse(ast);
+ Ok(format_items
+ .map(|res| res.map(Into::into))
+ .collect::<Result<Vec<_>, _>>()?)
+}
+
+/// Parse a sequence of items from the format description.
+///
+/// The syntax for the format description can be found in [the
+/// book](https://time-rs.github.io/book/api/format-description.html).
+///
+/// Unlike [`parse`], this function returns [`OwnedFormatItem`], which owns its contents. This means
+/// that there is no lifetime that needs to be handled.
+///
+/// [`OwnedFormatItem`]: crate::format_description::OwnedFormatItem
+pub fn parse_owned(
+ s: &str,
+) -> Result<crate::format_description::OwnedFormatItem, crate::error::InvalidFormatDescription> {
+ let lexed = lexer::lex(s.as_bytes());
+ let ast = ast::parse(lexed);
+ let format_items = format_item::parse(ast);
+ let items = format_items
+ .map(|res| res.map(Into::into))
+ .collect::<Result<Vec<_>, _>>()?
+ .into_boxed_slice();
+ Ok(crate::format_description::OwnedFormatItem::Compound(items))
+}
+
+/// A location within a string.
+#[derive(Clone, Copy)]
+struct Location {
+ /// The one-indexed line of the string.
+ line: usize,
+ /// The one-indexed column of the string.
+ column: usize,
+ /// The zero-indexed byte of the string.
+ byte: usize,
+}
+
+impl Location {
+ /// Offset the location by the provided amount.
+ ///
+ /// Note that this assumes the resulting location is on the same line as the original location.
+ #[must_use = "this does not modify the original value"]
+ const fn offset(&self, offset: usize) -> Self {
+ Self {
+ line: self.line,
+ column: self.column + offset,
+ byte: self.byte + offset,
+ }
+ }
+
+ /// Create an error with the provided message at this location.
+ const fn error(self, message: &'static str) -> ErrorInner {
+ ErrorInner {
+ _message: message,
+ _span: Span {
+ start: self,
+ end: self,
+ },
+ }
+ }
+}
+
+/// A start and end point within a string.
+#[derive(Clone, Copy)]
+struct Span {
+ #[allow(clippy::missing_docs_in_private_items)]
+ start: Location,
+ #[allow(clippy::missing_docs_in_private_items)]
+ end: Location,
+}
+
+impl Span {
+ /// Create a new `Span` from the provided start and end locations.
+ const fn start_end(start: Location, end: Location) -> Self {
+ Self { start, end }
+ }
+
+ /// Reduce this span to the provided range.
+ #[must_use = "this does not modify the original value"]
+ fn subspan(&self, range: impl Subspan) -> Self {
+ range.subspan(self)
+ }
+
+ /// Obtain a `Span` pointing at the start of the pre-existing span.
+ #[must_use = "this does not modify the original value"]
+ const fn shrink_to_start(&self) -> Self {
+ Self {
+ start: self.start,
+ end: self.start,
+ }
+ }
+
+ /// Obtain a `Span` pointing at the end of the pre-existing span.
+ #[must_use = "this does not modify the original value"]
+ const fn shrink_to_end(&self) -> Self {
+ Self {
+ start: self.end,
+ end: self.end,
+ }
+ }
+
+ /// Create an error with the provided message at this span.
+ const fn error(self, message: &'static str) -> ErrorInner {
+ ErrorInner {
+ _message: message,
+ _span: self,
+ }
+ }
+
+ /// Get the byte index that the span starts at.
+ const fn start_byte(&self) -> usize {
+ self.start.byte
+ }
+}
+
+/// A trait for types that can be used to reduce a `Span`.
+trait Subspan {
+ /// Reduce the provided `Span` to a new `Span`.
+ fn subspan(self, span: &Span) -> Span;
+}
+
+impl Subspan for RangeFrom<usize> {
+ fn subspan(self, span: &Span) -> Span {
+ assert_eq!(span.start.line, span.end.line);
+
+ Span {
+ start: Location {
+ line: span.start.line,
+ column: span.start.column + self.start,
+ byte: span.start.byte + self.start,
+ },
+ end: span.end,
+ }
+ }
+}
+
+impl Subspan for RangeTo<usize> {
+ fn subspan(self, span: &Span) -> Span {
+ assert_eq!(span.start.line, span.end.line);
+
+ Span {
+ start: span.start,
+ end: Location {
+ line: span.start.line,
+ column: span.start.column + self.end - 1,
+ byte: span.start.byte + self.end - 1,
+ },
+ }
+ }
+}
+
+/// The internal error type.
+struct ErrorInner {
+ /// The message displayed to the user.
+ _message: &'static str,
+ /// Where the error originated.
+ _span: Span,
+}
+
+/// A complete error description.
+struct Error {
+ /// The internal error.
+ _inner: ErrorInner,
+ /// The error needed for interoperability with the rest of `time`.
+ public: crate::error::InvalidFormatDescription,
+}
+
+impl From<Error> for crate::error::InvalidFormatDescription {
+ fn from(error: Error) -> Self {
+ error.public
+ }
+}
diff --git a/vendor/time/src/format_description/well_known/iso8601.rs b/vendor/time/src/format_description/well_known/iso8601.rs
new file mode 100644
index 000000000..f19181a92
--- /dev/null
+++ b/vendor/time/src/format_description/well_known/iso8601.rs
@@ -0,0 +1,233 @@
+//! The format described in ISO 8601.
+
+mod adt_hack;
+
+use core::num::NonZeroU8;
+
+pub use self::adt_hack::{DoNotRelyOnWhatThisIs, EncodedConfig};
+
+/// A configuration for [`Iso8601`] that only parses values.
+const PARSING_ONLY: EncodedConfig = Config {
+ formatted_components: FormattedComponents::None,
+ use_separators: false,
+ year_is_six_digits: false,
+ date_kind: DateKind::Calendar,
+ time_precision: TimePrecision::Hour {
+ decimal_digits: None,
+ },
+ offset_precision: OffsetPrecision::Hour,
+}
+.encode();
+
+/// The default configuration for [`Iso8601`].
+const DEFAULT_CONFIG: EncodedConfig = Config::DEFAULT.encode();
+
+/// The format described in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html).
+///
+/// This implementation is of ISO 8601-1:2019. It may not be compatible with other versions.
+///
+/// The const parameter `CONFIG` **must** be a value that was returned by [`Config::encode`].
+/// Passing any other value is **unspecified behavior**.
+///
+/// Example: 1997-11-21T09:55:06.000000000-06:00
+///
+/// # Examples
+#[cfg_attr(feature = "formatting", doc = "```rust")]
+#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")]
+/// # use time::format_description::well_known::Iso8601;
+/// # use time_macros::datetime;
+/// assert_eq!(
+/// datetime!(1997-11-12 9:55:06 -6:00).format(&Iso8601::DEFAULT)?,
+/// "1997-11-12T09:55:06.000000000-06:00"
+/// );
+/// # Ok::<_, time::Error>(())
+/// ```
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct Iso8601<const CONFIG: EncodedConfig = DEFAULT_CONFIG>;
+
+impl<const CONFIG: EncodedConfig> core::fmt::Debug for Iso8601<CONFIG> {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ f.debug_struct("Iso8601")
+ .field("config", &Config::decode(CONFIG))
+ .finish()
+ }
+}
+
+impl Iso8601<DEFAULT_CONFIG> {
+ /// An [`Iso8601`] with the default configuration.
+ ///
+ /// The following is the default behavior:
+ ///
+ /// - The configuration can be used for both formatting and parsing.
+ /// - The date, time, and UTC offset are all formatted.
+ /// - Separators (such as `-` and `:`) are included.
+ /// - The year contains four digits, such that the year must be between 0 and 9999.
+ /// - The date uses the calendar format.
+ /// - The time has precision to the second and nine decimal digits.
+ /// - The UTC offset has precision to the minute.
+ ///
+ /// If you need different behavior, use [`Config::DEFAULT`] and [`Config`]'s methods to create
+ /// a custom configuration.
+ pub const DEFAULT: Self = Self;
+}
+
+impl Iso8601<PARSING_ONLY> {
+ /// An [`Iso8601`] that can only be used for parsing. Using this to format a value is
+ /// unspecified behavior.
+ pub const PARSING: Self = Self;
+}
+
+/// Which components to format.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum FormattedComponents {
+ /// The configuration can only be used for parsing. Using this to format a value is
+ /// unspecified behavior.
+ None,
+ /// Format only the date.
+ Date,
+ /// Format only the time.
+ Time,
+ /// Format only the UTC offset.
+ Offset,
+ /// Format the date and time.
+ DateTime,
+ /// Format the date, time, and UTC offset.
+ DateTimeOffset,
+ /// Format the time and UTC offset.
+ TimeOffset,
+}
+
+/// Which format to use for the date.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum DateKind {
+ /// Use the year-month-day format.
+ Calendar,
+ /// Use the year-week-weekday format.
+ Week,
+ /// Use the week-ordinal format.
+ Ordinal,
+}
+
+/// The precision and number of decimal digits present for the time.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum TimePrecision {
+ /// Format the hour only. Minutes, seconds, and nanoseconds will be represented with the
+ /// specified number of decimal digits, if any.
+ Hour {
+ #[allow(clippy::missing_docs_in_private_items)]
+ decimal_digits: Option<NonZeroU8>,
+ },
+ /// Format the hour and minute. Seconds and nanoseconds will be represented with the specified
+ /// number of decimal digits, if any.
+ Minute {
+ #[allow(clippy::missing_docs_in_private_items)]
+ decimal_digits: Option<NonZeroU8>,
+ },
+ /// Format the hour, minute, and second. Nanoseconds will be represented with the specified
+ /// number of decimal digits, if any.
+ Second {
+ #[allow(clippy::missing_docs_in_private_items)]
+ decimal_digits: Option<NonZeroU8>,
+ },
+}
+
+/// The precision for the UTC offset.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum OffsetPrecision {
+ /// Format only the offset hour. Requires the offset minute to be zero.
+ Hour,
+ /// Format both the offset hour and minute.
+ Minute,
+}
+
+/// Configuration for [`Iso8601`].
+// This is only used as a const generic, so there's no need to have a number of implementations on
+// it.
+#[allow(missing_copy_implementations)]
+#[doc(alias = "EncodedConfig")] // People will likely search for `EncodedConfig`, so show them this.
+#[derive(Debug)]
+pub struct Config {
+ /// Which components, if any, will be formatted.
+ pub(crate) formatted_components: FormattedComponents,
+ /// Whether the format contains separators (such as `-` or `:`).
+ pub(crate) use_separators: bool,
+ /// Whether the year is six digits.
+ pub(crate) year_is_six_digits: bool,
+ /// The format used for the date.
+ pub(crate) date_kind: DateKind,
+ /// The precision and number of decimal digits present for the time.
+ pub(crate) time_precision: TimePrecision,
+ /// The precision for the UTC offset.
+ pub(crate) offset_precision: OffsetPrecision,
+}
+
+impl Config {
+ /// A configuration for the [`Iso8601`] format.
+ ///
+ /// The following is the default behavior:
+ ///
+ /// - The configuration can be used for both formatting and parsing.
+ /// - The date, time, and UTC offset are all formatted.
+ /// - Separators (such as `-` and `:`) are included.
+ /// - The year contains four digits, such that the year must be between 0 and 9999.
+ /// - The date uses the calendar format.
+ /// - The time has precision to the second and nine decimal digits.
+ /// - The UTC offset has precision to the minute.
+ ///
+ /// If you need different behavior, use the setter methods on this struct.
+ pub const DEFAULT: Self = Self {
+ formatted_components: FormattedComponents::DateTimeOffset,
+ use_separators: true,
+ year_is_six_digits: false,
+ date_kind: DateKind::Calendar,
+ time_precision: TimePrecision::Second {
+ decimal_digits: NonZeroU8::new(9),
+ },
+ offset_precision: OffsetPrecision::Minute,
+ };
+
+ /// Set whether the format the date, time, and/or UTC offset.
+ pub const fn set_formatted_components(self, formatted_components: FormattedComponents) -> Self {
+ Self {
+ formatted_components,
+ ..self
+ }
+ }
+
+ /// Set whether the format contains separators (such as `-` or `:`).
+ pub const fn set_use_separators(self, use_separators: bool) -> Self {
+ Self {
+ use_separators,
+ ..self
+ }
+ }
+
+ /// Set whether the year is six digits.
+ pub const fn set_year_is_six_digits(self, year_is_six_digits: bool) -> Self {
+ Self {
+ year_is_six_digits,
+ ..self
+ }
+ }
+
+ /// Set the format used for the date.
+ pub const fn set_date_kind(self, date_kind: DateKind) -> Self {
+ Self { date_kind, ..self }
+ }
+
+ /// Set the precision and number of decimal digits present for the time.
+ pub const fn set_time_precision(self, time_precision: TimePrecision) -> Self {
+ Self {
+ time_precision,
+ ..self
+ }
+ }
+
+ /// Set the precision for the UTC offset.
+ pub const fn set_offset_precision(self, offset_precision: OffsetPrecision) -> Self {
+ Self {
+ offset_precision,
+ ..self
+ }
+ }
+}
diff --git a/vendor/time/src/format_description/well_known/iso8601/adt_hack.rs b/vendor/time/src/format_description/well_known/iso8601/adt_hack.rs
new file mode 100644
index 000000000..757a68b18
--- /dev/null
+++ b/vendor/time/src/format_description/well_known/iso8601/adt_hack.rs
@@ -0,0 +1,249 @@
+//! Hackery to work around not being able to use ADTs in const generics on stable.
+
+use core::num::NonZeroU8;
+
+#[cfg(feature = "formatting")]
+use super::Iso8601;
+use super::{Config, DateKind, FormattedComponents as FC, OffsetPrecision, TimePrecision};
+
+// This provides a way to include `EncodedConfig` in documentation without displaying the type it is
+// aliased to.
+#[doc(hidden)]
+pub type DoNotRelyOnWhatThisIs = u128;
+
+/// An encoded [`Config`] that can be used as a const parameter to [`Iso8601`].
+///
+/// The type this is aliased to must not be relied upon. It can change in any release without
+/// notice.
+pub type EncodedConfig = DoNotRelyOnWhatThisIs;
+
+#[cfg(feature = "formatting")]
+impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
+ /// The user-provided configuration for the ISO 8601 format.
+ const CONFIG: Config = Config::decode(CONFIG);
+ /// Whether the date should be formatted.
+ pub(crate) const FORMAT_DATE: bool = matches!(
+ Self::CONFIG.formatted_components,
+ FC::Date | FC::DateTime | FC::DateTimeOffset
+ );
+ /// Whether the time should be formatted.
+ pub(crate) const FORMAT_TIME: bool = matches!(
+ Self::CONFIG.formatted_components,
+ FC::Time | FC::DateTime | FC::DateTimeOffset | FC::TimeOffset
+ );
+ /// Whether the UTC offset should be formatted.
+ pub(crate) const FORMAT_OFFSET: bool = matches!(
+ Self::CONFIG.formatted_components,
+ FC::Offset | FC::DateTimeOffset | FC::TimeOffset
+ );
+ /// Whether the year is six digits.
+ pub(crate) const YEAR_IS_SIX_DIGITS: bool = Self::CONFIG.year_is_six_digits;
+ /// Whether the format contains separators (such as `-` or `:`).
+ pub(crate) const USE_SEPARATORS: bool = Self::CONFIG.use_separators;
+ /// Which format to use for the date.
+ pub(crate) const DATE_KIND: DateKind = Self::CONFIG.date_kind;
+ /// The precision and number of decimal digits to use for the time.
+ pub(crate) const TIME_PRECISION: TimePrecision = Self::CONFIG.time_precision;
+ /// The precision for the UTC offset.
+ pub(crate) const OFFSET_PRECISION: OffsetPrecision = Self::CONFIG.offset_precision;
+}
+
+impl Config {
+ /// Encode the configuration, permitting it to be used as a const parameter of [`Iso8601`].
+ ///
+ /// The value returned by this method must only be used as a const parameter to [`Iso8601`]. Any
+ /// other usage is unspecified behavior.
+ pub const fn encode(&self) -> EncodedConfig {
+ let mut bytes = [0; EncodedConfig::BITS as usize / 8];
+
+ bytes[0] = match self.formatted_components {
+ FC::None => 0,
+ FC::Date => 1,
+ FC::Time => 2,
+ FC::Offset => 3,
+ FC::DateTime => 4,
+ FC::DateTimeOffset => 5,
+ FC::TimeOffset => 6,
+ };
+ bytes[1] = self.use_separators as _;
+ bytes[2] = self.year_is_six_digits as _;
+ bytes[3] = match self.date_kind {
+ DateKind::Calendar => 0,
+ DateKind::Week => 1,
+ DateKind::Ordinal => 2,
+ };
+ bytes[4] = match self.time_precision {
+ TimePrecision::Hour { .. } => 0,
+ TimePrecision::Minute { .. } => 1,
+ TimePrecision::Second { .. } => 2,
+ };
+ bytes[5] = match self.time_precision {
+ TimePrecision::Hour { decimal_digits }
+ | TimePrecision::Minute { decimal_digits }
+ | TimePrecision::Second { decimal_digits } => match decimal_digits {
+ None => 0,
+ Some(decimal_digits) => decimal_digits.get(),
+ },
+ };
+ bytes[6] = match self.offset_precision {
+ OffsetPrecision::Hour => 0,
+ OffsetPrecision::Minute => 1,
+ };
+
+ EncodedConfig::from_be_bytes(bytes)
+ }
+
+ /// Decode the configuration. The configuration must have been generated from
+ /// [`Config::encode`].
+ pub(super) const fn decode(encoded: EncodedConfig) -> Self {
+ let bytes = encoded.to_be_bytes();
+
+ let formatted_components = match bytes[0] {
+ 0 => FC::None,
+ 1 => FC::Date,
+ 2 => FC::Time,
+ 3 => FC::Offset,
+ 4 => FC::DateTime,
+ 5 => FC::DateTimeOffset,
+ 6 => FC::TimeOffset,
+ _ => panic!("invalid configuration"),
+ };
+ let use_separators = match bytes[1] {
+ 0 => false,
+ 1 => true,
+ _ => panic!("invalid configuration"),
+ };
+ let year_is_six_digits = match bytes[2] {
+ 0 => false,
+ 1 => true,
+ _ => panic!("invalid configuration"),
+ };
+ let date_kind = match bytes[3] {
+ 0 => DateKind::Calendar,
+ 1 => DateKind::Week,
+ 2 => DateKind::Ordinal,
+ _ => panic!("invalid configuration"),
+ };
+ let time_precision = match bytes[4] {
+ 0 => TimePrecision::Hour {
+ decimal_digits: NonZeroU8::new(bytes[5]),
+ },
+ 1 => TimePrecision::Minute {
+ decimal_digits: NonZeroU8::new(bytes[5]),
+ },
+ 2 => TimePrecision::Second {
+ decimal_digits: NonZeroU8::new(bytes[5]),
+ },
+ _ => panic!("invalid configuration"),
+ };
+ let offset_precision = match bytes[6] {
+ 0 => OffsetPrecision::Hour,
+ 1 => OffsetPrecision::Minute,
+ _ => panic!("invalid configuration"),
+ };
+
+ // No `for` loops in `const fn`.
+ let mut idx = 7; // first unused byte
+ while idx < EncodedConfig::BITS as usize / 8 {
+ assert!(bytes[idx] == 0, "invalid configuration");
+ idx += 1;
+ }
+
+ Self {
+ formatted_components,
+ use_separators,
+ year_is_six_digits,
+ date_kind,
+ time_precision,
+ offset_precision,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ macro_rules! eq {
+ ($a:expr, $b:expr) => {{
+ let a = $a;
+ let b = $b;
+ a.formatted_components == b.formatted_components
+ && a.use_separators == b.use_separators
+ && a.year_is_six_digits == b.year_is_six_digits
+ && a.date_kind == b.date_kind
+ && a.time_precision == b.time_precision
+ && a.offset_precision == b.offset_precision
+ }};
+ }
+
+ #[test]
+ fn encoding_roundtrip() {
+ macro_rules! assert_roundtrip {
+ ($config:expr) => {
+ let config = $config;
+ let encoded = config.encode();
+ let decoded = Config::decode(encoded);
+ assert!(eq!(config, decoded));
+ };
+ }
+
+ assert_roundtrip!(Config::DEFAULT);
+ assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::None));
+ assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Date));
+ assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Time));
+ assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Offset));
+ assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::DateTime));
+ assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::DateTimeOffset));
+ assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::TimeOffset));
+ assert_roundtrip!(Config::DEFAULT.set_use_separators(false));
+ assert_roundtrip!(Config::DEFAULT.set_use_separators(true));
+ assert_roundtrip!(Config::DEFAULT.set_year_is_six_digits(false));
+ assert_roundtrip!(Config::DEFAULT.set_year_is_six_digits(true));
+ assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Calendar));
+ assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Week));
+ assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Ordinal));
+ assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Hour {
+ decimal_digits: None,
+ }));
+ assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute {
+ decimal_digits: None,
+ }));
+ assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second {
+ decimal_digits: None,
+ }));
+ assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Hour {
+ decimal_digits: NonZeroU8::new(1),
+ }));
+ assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute {
+ decimal_digits: NonZeroU8::new(1),
+ }));
+ assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second {
+ decimal_digits: NonZeroU8::new(1),
+ }));
+ assert_roundtrip!(Config::DEFAULT.set_offset_precision(OffsetPrecision::Hour));
+ assert_roundtrip!(Config::DEFAULT.set_offset_precision(OffsetPrecision::Minute));
+ }
+
+ macro_rules! assert_decode_fail {
+ ($encoding:expr) => {
+ assert!(
+ std::panic::catch_unwind(|| {
+ Config::decode($encoding);
+ })
+ .is_err()
+ );
+ };
+ }
+
+ #[test]
+ fn decode_fail() {
+ assert_decode_fail!(0x07_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00);
+ assert_decode_fail!(0x00_02_00_00_00_00_00_00_00_00_00_00_00_00_00_00);
+ assert_decode_fail!(0x00_00_02_00_00_00_00_00_00_00_00_00_00_00_00_00);
+ assert_decode_fail!(0x00_00_00_03_00_00_00_00_00_00_00_00_00_00_00_00);
+ assert_decode_fail!(0x00_00_00_00_03_00_00_00_00_00_00_00_00_00_00_00);
+ assert_decode_fail!(0x00_00_00_00_00_00_02_00_00_00_00_00_00_00_00_00);
+ assert_decode_fail!(0x00_00_00_00_00_00_00_01_00_00_00_00_00_00_00_00);
+ }
+}
diff --git a/vendor/time/src/format_description/well_known/rfc2822.rs b/vendor/time/src/format_description/well_known/rfc2822.rs
new file mode 100644
index 000000000..3c890ab10
--- /dev/null
+++ b/vendor/time/src/format_description/well_known/rfc2822.rs
@@ -0,0 +1,30 @@
+//! The format described in RFC 2822.
+
+/// The format described in [RFC 2822](https://tools.ietf.org/html/rfc2822#section-3.3).
+///
+/// Example: Fri, 21 Nov 1997 09:55:06 -0600
+///
+/// # Examples
+#[cfg_attr(feature = "parsing", doc = "```rust")]
+#[cfg_attr(not(feature = "parsing"), doc = "```rust,ignore")]
+/// # use time::{format_description::well_known::Rfc2822, OffsetDateTime};
+/// use time_macros::datetime;
+/// assert_eq!(
+/// OffsetDateTime::parse("Sat, 12 Jun 1993 13:25:19 GMT", &Rfc2822)?,
+/// datetime!(1993-06-12 13:25:19 +00:00)
+/// );
+/// # Ok::<_, time::Error>(())
+/// ```
+///
+#[cfg_attr(feature = "formatting", doc = "```rust")]
+#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")]
+/// # use time::format_description::well_known::Rfc2822;
+/// # use time_macros::datetime;
+/// assert_eq!(
+/// datetime!(1997-11-21 09:55:06 -06:00).format(&Rfc2822)?,
+/// "Fri, 21 Nov 1997 09:55:06 -0600"
+/// );
+/// # Ok::<_, time::Error>(())
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Rfc2822;
diff --git a/vendor/time/src/format_description/well_known/rfc3339.rs b/vendor/time/src/format_description/well_known/rfc3339.rs
new file mode 100644
index 000000000..f0873cbac
--- /dev/null
+++ b/vendor/time/src/format_description/well_known/rfc3339.rs
@@ -0,0 +1,30 @@
+//! The format described in RFC 3339.
+
+/// The format described in [RFC 3339](https://tools.ietf.org/html/rfc3339#section-5.6).
+///
+/// Format example: 1985-04-12T23:20:50.52Z
+///
+/// # Examples
+#[cfg_attr(feature = "parsing", doc = "```rust")]
+#[cfg_attr(not(feature = "parsing"), doc = "```rust,ignore")]
+/// # use time::{format_description::well_known::Rfc3339, OffsetDateTime};
+/// # use time_macros::datetime;
+/// assert_eq!(
+/// OffsetDateTime::parse("1985-04-12T23:20:50.52Z", &Rfc3339)?,
+/// datetime!(1985-04-12 23:20:50.52 +00:00)
+/// );
+/// # Ok::<_, time::Error>(())
+/// ```
+///
+#[cfg_attr(feature = "formatting", doc = "```rust")]
+#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")]
+/// # use time::format_description::well_known::Rfc3339;
+/// # use time_macros::datetime;
+/// assert_eq!(
+/// datetime!(1985-04-12 23:20:50.52 +00:00).format(&Rfc3339)?,
+/// "1985-04-12T23:20:50.52Z"
+/// );
+/// # Ok::<_, time::Error>(())
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Rfc3339;