From 43a97878ce14b72f0981164f87f2e35e14151312 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:22:09 +0200 Subject: Adding upstream version 110.0.1. Signed-off-by: Daniel Baumann --- third_party/rust/time/src/serde/iso8601.rs | 77 ++++++ third_party/rust/time/src/serde/mod.rs | 375 +++++++++++++++++++++++++++ third_party/rust/time/src/serde/rfc2822.rs | 72 +++++ third_party/rust/time/src/serde/rfc3339.rs | 72 +++++ third_party/rust/time/src/serde/timestamp.rs | 60 +++++ third_party/rust/time/src/serde/visitor.rs | 316 ++++++++++++++++++++++ 6 files changed, 972 insertions(+) create mode 100644 third_party/rust/time/src/serde/iso8601.rs create mode 100644 third_party/rust/time/src/serde/mod.rs create mode 100644 third_party/rust/time/src/serde/rfc2822.rs create mode 100644 third_party/rust/time/src/serde/rfc3339.rs create mode 100644 third_party/rust/time/src/serde/timestamp.rs create mode 100644 third_party/rust/time/src/serde/visitor.rs (limited to 'third_party/rust/time/src/serde') diff --git a/third_party/rust/time/src/serde/iso8601.rs b/third_party/rust/time/src/serde/iso8601.rs new file mode 100644 index 0000000000..75deb62f1a --- /dev/null +++ b/third_party/rust/time/src/serde/iso8601.rs @@ -0,0 +1,77 @@ +//! Use the well-known [ISO 8601 format] when serializing and deserializing an [`OffsetDateTime`]. +//! +//! Use this module in combination with serde's [`#[with]`][with] attribute. +//! +//! [ISO 8601 format]: https://www.iso.org/iso-8601-date-and-time-format.html +//! [with]: https://serde.rs/field-attrs.html#with + +#[cfg(feature = "parsing")] +use core::marker::PhantomData; + +#[cfg(feature = "formatting")] +use serde::ser::Error as _; +#[cfg(feature = "parsing")] +use serde::Deserializer; +#[cfg(feature = "formatting")] +use serde::{Serialize, Serializer}; + +#[cfg(feature = "parsing")] +use super::Visitor; +use crate::format_description::well_known::iso8601::{Config, EncodedConfig}; +use crate::format_description::well_known::Iso8601; +use crate::OffsetDateTime; + +/// The configuration of ISO 8601 used for serde implementations. +pub(crate) const SERDE_CONFIG: EncodedConfig = + Config::DEFAULT.set_year_is_six_digits(true).encode(); + +/// Serialize an [`OffsetDateTime`] using the well-known ISO 8601 format. +#[cfg(feature = "formatting")] +pub fn serialize( + datetime: &OffsetDateTime, + serializer: S, +) -> Result { + datetime + .format(&Iso8601::) + .map_err(S::Error::custom)? + .serialize(serializer) +} + +/// Deserialize an [`OffsetDateTime`] from its ISO 8601 representation. +#[cfg(feature = "parsing")] +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result { + deserializer.deserialize_any(Visitor::>(PhantomData)) +} + +/// Use the well-known ISO 8601 format when serializing and deserializing an +/// [`Option`]. +/// +/// Use this module in combination with serde's [`#[with]`][with] attribute. +/// +/// [ISO 8601 format]: https://www.iso.org/iso-8601-date-and-time-format.html +/// [with]: https://serde.rs/field-attrs.html#with +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an [`Option`] using the well-known ISO 8601 format. + #[cfg(feature = "formatting")] + pub fn serialize( + option: &Option, + serializer: S, + ) -> Result { + option + .map(|odt| odt.format(&Iso8601::)) + .transpose() + .map_err(S::Error::custom)? + .serialize(serializer) + } + + /// Deserialize an [`Option`] from its ISO 8601 representation. + #[cfg(feature = "parsing")] + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result, D::Error> { + deserializer.deserialize_option(Visitor::>>(PhantomData)) + } +} diff --git a/third_party/rust/time/src/serde/mod.rs b/third_party/rust/time/src/serde/mod.rs new file mode 100644 index 0000000000..e9f6d4394c --- /dev/null +++ b/third_party/rust/time/src/serde/mod.rs @@ -0,0 +1,375 @@ +//! Differential formats for serde. +// This also includes the serde implementations for all types. This doesn't need to be externally +// documented, though. + +// Types with guaranteed stable serde representations. Strings are avoided to allow for optimal +// representations in various binary forms. + +/// Consume the next item in a sequence. +macro_rules! item { + ($seq:expr, $name:literal) => { + $seq.next_element()? + .ok_or_else(|| ::custom(concat!("expected ", $name))) + }; +} + +#[cfg(any(feature = "formatting", feature = "parsing"))] +pub mod iso8601; +#[cfg(any(feature = "formatting", feature = "parsing"))] +pub mod rfc2822; +#[cfg(any(feature = "formatting", feature = "parsing"))] +pub mod rfc3339; +pub mod timestamp; +mod visitor; + +use core::marker::PhantomData; + +#[cfg(feature = "serde-human-readable")] +use serde::ser::Error as _; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +/// Generate a custom serializer and deserializer from the provided string. +/// +/// The syntax accepted by this macro is the same as [`format_description::parse()`], which can +/// be found in [the book](https://time-rs.github.io/book/api/format-description.html). +/// +/// # Usage +/// +/// Invoked as `serde::format_description!(mod_name, Date, "")`. This puts a +/// module named `mod_name` in the current scope that can be used to format `Date` structs. A +/// submodule (`mod_name::option`) is also generated for `Option`. Both modules are only +/// visible in the current scope. +/// +/// The returned `Option` will contain a deserialized value if present and `None` if the field +/// is present but the value is `null` (or the equivalent in other formats). To return `None` +/// when the field is not present, you should use `#[serde(default)]` on the field. +/// +/// # Examples +/// +/// ```rust,no_run +/// # use time::OffsetDateTime; +#[cfg_attr( + all(feature = "formatting", feature = "parsing"), + doc = "use ::serde::{Serialize, Deserialize};" +)] +#[cfg_attr( + all(feature = "formatting", not(feature = "parsing")), + doc = "use ::serde::Serialize;" +)] +#[cfg_attr( + all(not(feature = "formatting"), feature = "parsing"), + doc = "use ::serde::Deserialize;" +)] +/// use time::serde; +/// +/// // Makes a module `mod my_format { ... }`. +/// serde::format_description!(my_format, OffsetDateTime, "hour=[hour], minute=[minute]"); +#[cfg_attr( + all(feature = "formatting", feature = "parsing"), + doc = "#[derive(Serialize, Deserialize)]" +)] +#[cfg_attr( + all(feature = "formatting", not(feature = "parsing")), + doc = "#[derive(Serialize)]" +)] +#[cfg_attr( + all(not(feature = "formatting"), feature = "parsing"), + doc = "#[derive(Deserialize)]" +)] +/// # #[allow(dead_code)] +/// struct SerializesWithCustom { +/// #[serde(with = "my_format")] +/// dt: OffsetDateTime, +/// #[serde(with = "my_format::option")] +/// maybe_dt: Option, +/// } +/// ``` +/// +/// [`format_description::parse()`]: crate::format_description::parse() +#[cfg(all(feature = "macros", any(feature = "formatting", feature = "parsing"),))] +pub use time_macros::serde_format_description as format_description; + +use self::visitor::Visitor; +#[cfg(feature = "parsing")] +use crate::format_description::{modifier, Component, FormatItem}; +use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday}; + +// region: Date +/// The format used when serializing and deserializing a human-readable `Date`. +#[cfg(feature = "parsing")] +const DATE_FORMAT: &[FormatItem<'_>] = &[ + FormatItem::Component(Component::Year(modifier::Year::default())), + FormatItem::Literal(b"-"), + FormatItem::Component(Component::Month(modifier::Month::default())), + FormatItem::Literal(b"-"), + FormatItem::Component(Component::Day(modifier::Day::default())), +]; + +impl Serialize for Date { + fn serialize(&self, serializer: S) -> Result { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + return serializer.serialize_str(&match self.format(&DATE_FORMAT) { + Ok(s) => s, + Err(_) => return Err(S::Error::custom("failed formatting `Date`")), + }); + } + + (self.year(), self.ordinal()).serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for Date { + fn deserialize>(deserializer: D) -> Result { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::(PhantomData)) + } else { + deserializer.deserialize_tuple(2, Visitor::(PhantomData)) + } + } +} +// endregion date + +// region: Duration +impl Serialize for Duration { + fn serialize(&self, serializer: S) -> Result { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + return serializer.collect_str(&format_args!( + "{}.{:>09}", + self.whole_seconds(), + self.subsec_nanoseconds().abs() + )); + } + + (self.whole_seconds(), self.subsec_nanoseconds()).serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for Duration { + fn deserialize>(deserializer: D) -> Result { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::(PhantomData)) + } else { + deserializer.deserialize_tuple(2, Visitor::(PhantomData)) + } + } +} +// endregion Duration + +// region: OffsetDateTime +/// The format used when serializing and deserializing a human-readable `OffsetDateTime`. +#[cfg(feature = "parsing")] +const OFFSET_DATE_TIME_FORMAT: &[FormatItem<'_>] = &[ + FormatItem::Compound(DATE_FORMAT), + FormatItem::Literal(b" "), + FormatItem::Compound(TIME_FORMAT), + FormatItem::Literal(b" "), + FormatItem::Compound(UTC_OFFSET_FORMAT), +]; + +impl Serialize for OffsetDateTime { + fn serialize(&self, serializer: S) -> Result { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + return serializer.serialize_str(&match self.format(&OFFSET_DATE_TIME_FORMAT) { + Ok(s) => s, + Err(_) => return Err(S::Error::custom("failed formatting `OffsetDateTime`")), + }); + } + + ( + self.year(), + self.ordinal(), + self.hour(), + self.minute(), + self.second(), + self.nanosecond(), + self.offset.whole_hours(), + self.offset.minutes_past_hour(), + self.offset.seconds_past_minute(), + ) + .serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for OffsetDateTime { + fn deserialize>(deserializer: D) -> Result { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::(PhantomData)) + } else { + deserializer.deserialize_tuple(9, Visitor::(PhantomData)) + } + } +} +// endregion OffsetDateTime + +// region: PrimitiveDateTime +/// The format used when serializing and deserializing a human-readable `PrimitiveDateTime`. +#[cfg(feature = "parsing")] +const PRIMITIVE_DATE_TIME_FORMAT: &[FormatItem<'_>] = &[ + FormatItem::Compound(DATE_FORMAT), + FormatItem::Literal(b" "), + FormatItem::Compound(TIME_FORMAT), +]; + +impl Serialize for PrimitiveDateTime { + fn serialize(&self, serializer: S) -> Result { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + return serializer.serialize_str(&match self.format(&PRIMITIVE_DATE_TIME_FORMAT) { + Ok(s) => s, + Err(_) => return Err(::custom("failed formatting `PrimitiveDateTime`")), + }); + } + + ( + self.year(), + self.ordinal(), + self.hour(), + self.minute(), + self.second(), + self.nanosecond(), + ) + .serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for PrimitiveDateTime { + fn deserialize>(deserializer: D) -> Result { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::(PhantomData)) + } else { + deserializer.deserialize_tuple(6, Visitor::(PhantomData)) + } + } +} +// endregion PrimitiveDateTime + +// region: Time +/// The format used when serializing and deserializing a human-readable `Time`. +#[cfg(feature = "parsing")] +const TIME_FORMAT: &[FormatItem<'_>] = &[ + FormatItem::Component(Component::Hour(::default())), + FormatItem::Literal(b":"), + FormatItem::Component(Component::Minute(::default())), + FormatItem::Literal(b":"), + FormatItem::Component(Component::Second(::default())), + FormatItem::Literal(b"."), + FormatItem::Component(Component::Subsecond(::default())), +]; + +impl Serialize for Time { + fn serialize(&self, serializer: S) -> Result { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + return serializer.serialize_str(&match self.format(&TIME_FORMAT) { + Ok(s) => s, + Err(_) => return Err(S::Error::custom("failed formatting `Time`")), + }); + } + + (self.hour(), self.minute(), self.second(), self.nanosecond()).serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for Time { + fn deserialize>(deserializer: D) -> Result { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::(PhantomData)) + } else { + deserializer.deserialize_tuple(4, Visitor::(PhantomData)) + } + } +} +// endregion Time + +// region: UtcOffset +/// The format used when serializing and deserializing a human-readable `UtcOffset`. +#[cfg(feature = "parsing")] +const UTC_OFFSET_FORMAT: &[FormatItem<'_>] = &[ + FormatItem::Component(Component::OffsetHour(modifier::OffsetHour::default())), + FormatItem::Literal(b":"), + FormatItem::Component(Component::OffsetMinute(modifier::OffsetMinute::default())), + FormatItem::Literal(b":"), + FormatItem::Component(Component::OffsetSecond(modifier::OffsetSecond::default())), +]; + +impl Serialize for UtcOffset { + fn serialize(&self, serializer: S) -> Result { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + return serializer.serialize_str(&match self.format(&UTC_OFFSET_FORMAT) { + Ok(s) => s, + Err(_) => return Err(S::Error::custom("failed formatting `UtcOffset`")), + }); + } + + ( + self.whole_hours(), + self.minutes_past_hour(), + self.seconds_past_minute(), + ) + .serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for UtcOffset { + fn deserialize>(deserializer: D) -> Result { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::(PhantomData)) + } else { + deserializer.deserialize_tuple(3, Visitor::(PhantomData)) + } + } +} +// endregion UtcOffset + +// region: Weekday +impl Serialize for Weekday { + fn serialize(&self, serializer: S) -> Result { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + #[cfg(not(feature = "std"))] + use alloc::string::ToString; + return self.to_string().serialize(serializer); + } + + self.number_from_monday().serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for Weekday { + fn deserialize>(deserializer: D) -> Result { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::(PhantomData)) + } else { + deserializer.deserialize_u8(Visitor::(PhantomData)) + } + } +} +// endregion Weekday + +// region: Month +impl Serialize for Month { + fn serialize(&self, serializer: S) -> Result { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + #[cfg(not(feature = "std"))] + use alloc::string::String; + return self.to_string().serialize(serializer); + } + + (*self as u8).serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for Month { + fn deserialize>(deserializer: D) -> Result { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::(PhantomData)) + } else { + deserializer.deserialize_u8(Visitor::(PhantomData)) + } + } +} +// endregion Month diff --git a/third_party/rust/time/src/serde/rfc2822.rs b/third_party/rust/time/src/serde/rfc2822.rs new file mode 100644 index 0000000000..eca90f5204 --- /dev/null +++ b/third_party/rust/time/src/serde/rfc2822.rs @@ -0,0 +1,72 @@ +//! Use the well-known [RFC2822 format] when serializing and deserializing an [`OffsetDateTime`]. +//! +//! Use this module in combination with serde's [`#[with]`][with] attribute. +//! +//! [RFC2822 format]: https://tools.ietf.org/html/rfc2822#section-3.3 +//! [with]: https://serde.rs/field-attrs.html#with + +#[cfg(feature = "parsing")] +use core::marker::PhantomData; + +#[cfg(feature = "formatting")] +use serde::ser::Error as _; +#[cfg(feature = "parsing")] +use serde::Deserializer; +#[cfg(feature = "formatting")] +use serde::{Serialize, Serializer}; + +#[cfg(feature = "parsing")] +use super::Visitor; +use crate::format_description::well_known::Rfc2822; +use crate::OffsetDateTime; + +/// Serialize an [`OffsetDateTime`] using the well-known RFC2822 format. +#[cfg(feature = "formatting")] +pub fn serialize( + datetime: &OffsetDateTime, + serializer: S, +) -> Result { + datetime + .format(&Rfc2822) + .map_err(S::Error::custom)? + .serialize(serializer) +} + +/// Deserialize an [`OffsetDateTime`] from its RFC2822 representation. +#[cfg(feature = "parsing")] +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result { + deserializer.deserialize_str(Visitor::(PhantomData)) +} + +/// Use the well-known [RFC2822 format] when serializing and deserializing an +/// [`Option`]. +/// +/// Use this module in combination with serde's [`#[with]`][with] attribute. +/// +/// [RFC2822 format]: https://tools.ietf.org/html/rfc2822#section-3.3 +/// [with]: https://serde.rs/field-attrs.html#with +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an [`Option`] using the well-known RFC2822 format. + #[cfg(feature = "formatting")] + pub fn serialize( + option: &Option, + serializer: S, + ) -> Result { + option + .map(|odt| odt.format(&Rfc2822)) + .transpose() + .map_err(S::Error::custom)? + .serialize(serializer) + } + + /// Deserialize an [`Option`] from its RFC2822 representation. + #[cfg(feature = "parsing")] + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result, D::Error> { + deserializer.deserialize_option(Visitor::>(PhantomData)) + } +} diff --git a/third_party/rust/time/src/serde/rfc3339.rs b/third_party/rust/time/src/serde/rfc3339.rs new file mode 100644 index 0000000000..b1ffb25130 --- /dev/null +++ b/third_party/rust/time/src/serde/rfc3339.rs @@ -0,0 +1,72 @@ +//! Use the well-known [RFC3339 format] when serializing and deserializing an [`OffsetDateTime`]. +//! +//! Use this module in combination with serde's [`#[with]`][with] attribute. +//! +//! [RFC3339 format]: https://tools.ietf.org/html/rfc3339#section-5.6 +//! [with]: https://serde.rs/field-attrs.html#with + +#[cfg(feature = "parsing")] +use core::marker::PhantomData; + +#[cfg(feature = "formatting")] +use serde::ser::Error as _; +#[cfg(feature = "parsing")] +use serde::Deserializer; +#[cfg(feature = "formatting")] +use serde::{Serialize, Serializer}; + +#[cfg(feature = "parsing")] +use super::Visitor; +use crate::format_description::well_known::Rfc3339; +use crate::OffsetDateTime; + +/// Serialize an [`OffsetDateTime`] using the well-known RFC3339 format. +#[cfg(feature = "formatting")] +pub fn serialize( + datetime: &OffsetDateTime, + serializer: S, +) -> Result { + datetime + .format(&Rfc3339) + .map_err(S::Error::custom)? + .serialize(serializer) +} + +/// Deserialize an [`OffsetDateTime`] from its RFC3339 representation. +#[cfg(feature = "parsing")] +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result { + deserializer.deserialize_str(Visitor::(PhantomData)) +} + +/// Use the well-known [RFC3339 format] when serializing and deserializing an +/// [`Option`]. +/// +/// Use this module in combination with serde's [`#[with]`][with] attribute. +/// +/// [RFC3339 format]: https://tools.ietf.org/html/rfc3339#section-5.6 +/// [with]: https://serde.rs/field-attrs.html#with +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an [`Option`] using the well-known RFC3339 format. + #[cfg(feature = "formatting")] + pub fn serialize( + option: &Option, + serializer: S, + ) -> Result { + option + .map(|odt| odt.format(&Rfc3339)) + .transpose() + .map_err(S::Error::custom)? + .serialize(serializer) + } + + /// Deserialize an [`Option`] from its RFC3339 representation. + #[cfg(feature = "parsing")] + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result, D::Error> { + deserializer.deserialize_option(Visitor::>(PhantomData)) + } +} diff --git a/third_party/rust/time/src/serde/timestamp.rs b/third_party/rust/time/src/serde/timestamp.rs new file mode 100644 index 0000000000..d86e6b9336 --- /dev/null +++ b/third_party/rust/time/src/serde/timestamp.rs @@ -0,0 +1,60 @@ +//! Treat an [`OffsetDateTime`] as a [Unix timestamp] for the purposes of serde. +//! +//! Use this module in combination with serde's [`#[with]`][with] attribute. +//! +//! When deserializing, the offset is assumed to be UTC. +//! +//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +//! [with]: https://serde.rs/field-attrs.html#with + +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +use crate::OffsetDateTime; + +/// Serialize an `OffsetDateTime` as its Unix timestamp +pub fn serialize( + datetime: &OffsetDateTime, + serializer: S, +) -> Result { + datetime.unix_timestamp().serialize(serializer) +} + +/// Deserialize an `OffsetDateTime` from its Unix timestamp +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result { + OffsetDateTime::from_unix_timestamp(<_>::deserialize(deserializer)?) + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) +} + +/// Treat an `Option` as a [Unix timestamp] for the purposes of +/// serde. +/// +/// Use this module in combination with serde's [`#[with]`][with] attribute. +/// +/// When deserializing, the offset is assumed to be UTC. +/// +/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +/// [with]: https://serde.rs/field-attrs.html#with +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an `Option` as its Unix timestamp + pub fn serialize( + option: &Option, + serializer: S, + ) -> Result { + option + .map(OffsetDateTime::unix_timestamp) + .serialize(serializer) + } + + /// Deserialize an `Option` from its Unix timestamp + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result, D::Error> { + Option::deserialize(deserializer)? + .map(OffsetDateTime::from_unix_timestamp) + .transpose() + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) + } +} diff --git a/third_party/rust/time/src/serde/visitor.rs b/third_party/rust/time/src/serde/visitor.rs new file mode 100644 index 0000000000..e61989afde --- /dev/null +++ b/third_party/rust/time/src/serde/visitor.rs @@ -0,0 +1,316 @@ +//! Serde visitor for various types. + +use core::fmt; +use core::marker::PhantomData; + +use serde::de; +#[cfg(feature = "parsing")] +use serde::Deserializer; + +#[cfg(feature = "parsing")] +use super::{ + DATE_FORMAT, OFFSET_DATE_TIME_FORMAT, PRIMITIVE_DATE_TIME_FORMAT, TIME_FORMAT, + UTC_OFFSET_FORMAT, +}; +use crate::error::ComponentRange; +#[cfg(feature = "parsing")] +use crate::format_description::well_known::*; +use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday}; + +/// A serde visitor for various types. +pub(super) struct Visitor(pub(super) PhantomData); + +impl<'a> de::Visitor<'a> for Visitor { + type Value = Date; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `Date`") + } + + #[cfg(feature = "parsing")] + fn visit_str(self, value: &str) -> Result { + Date::parse(value, &DATE_FORMAT).map_err(E::custom) + } + + fn visit_seq>(self, mut seq: A) -> Result { + let year = item!(seq, "year")?; + let ordinal = item!(seq, "day of year")?; + Date::from_ordinal_date(year, ordinal).map_err(ComponentRange::into_de_error) + } +} + +impl<'a> de::Visitor<'a> for Visitor { + type Value = Duration; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `Duration`") + } + + fn visit_str(self, value: &str) -> Result { + let (seconds, nanoseconds) = value.split_once('.').ok_or_else(|| { + de::Error::invalid_value(de::Unexpected::Str(value), &"a decimal point") + })?; + + let seconds = seconds + .parse() + .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(seconds), &"seconds"))?; + let mut nanoseconds = nanoseconds.parse().map_err(|_| { + de::Error::invalid_value(de::Unexpected::Str(nanoseconds), &"nanoseconds") + })?; + + if seconds < 0 { + nanoseconds *= -1; + } + + Ok(Duration::new(seconds, nanoseconds)) + } + + fn visit_seq>(self, mut seq: A) -> Result { + let seconds = item!(seq, "seconds")?; + let nanoseconds = item!(seq, "nanoseconds")?; + Ok(Duration::new(seconds, nanoseconds)) + } +} + +impl<'a> de::Visitor<'a> for Visitor { + type Value = OffsetDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("an `OffsetDateTime`") + } + + #[cfg(feature = "parsing")] + fn visit_str(self, value: &str) -> Result { + OffsetDateTime::parse(value, &OFFSET_DATE_TIME_FORMAT).map_err(E::custom) + } + + fn visit_seq>(self, mut seq: A) -> Result { + let year = item!(seq, "year")?; + let ordinal = item!(seq, "day of year")?; + let hour = item!(seq, "hour")?; + let minute = item!(seq, "minute")?; + let second = item!(seq, "second")?; + let nanosecond = item!(seq, "nanosecond")?; + let offset_hours = item!(seq, "offset hours")?; + let offset_minutes = item!(seq, "offset minutes")?; + let offset_seconds = item!(seq, "offset seconds")?; + + Date::from_ordinal_date(year, ordinal) + .and_then(|date| date.with_hms_nano(hour, minute, second, nanosecond)) + .and_then(|datetime| { + UtcOffset::from_hms(offset_hours, offset_minutes, offset_seconds) + .map(|offset| datetime.assume_offset(offset)) + }) + .map_err(ComponentRange::into_de_error) + } +} + +impl<'a> de::Visitor<'a> for Visitor { + type Value = PrimitiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `PrimitiveDateTime`") + } + + #[cfg(feature = "parsing")] + fn visit_str(self, value: &str) -> Result { + PrimitiveDateTime::parse(value, &PRIMITIVE_DATE_TIME_FORMAT).map_err(E::custom) + } + + fn visit_seq>(self, mut seq: A) -> Result { + let year = item!(seq, "year")?; + let ordinal = item!(seq, "day of year")?; + let hour = item!(seq, "hour")?; + let minute = item!(seq, "minute")?; + let second = item!(seq, "second")?; + let nanosecond = item!(seq, "nanosecond")?; + + Date::from_ordinal_date(year, ordinal) + .and_then(|date| date.with_hms_nano(hour, minute, second, nanosecond)) + .map_err(ComponentRange::into_de_error) + } +} + +impl<'a> de::Visitor<'a> for Visitor