diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/rust/time/src/serde | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/time/src/serde')
-rw-r--r-- | third_party/rust/time/src/serde/iso8601.rs | 77 | ||||
-rw-r--r-- | third_party/rust/time/src/serde/mod.rs | 375 | ||||
-rw-r--r-- | third_party/rust/time/src/serde/rfc2822.rs | 72 | ||||
-rw-r--r-- | third_party/rust/time/src/serde/rfc3339.rs | 72 | ||||
-rw-r--r-- | third_party/rust/time/src/serde/timestamp.rs | 60 | ||||
-rw-r--r-- | third_party/rust/time/src/serde/visitor.rs | 316 |
6 files changed, 972 insertions, 0 deletions
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<S: Serializer>( + datetime: &OffsetDateTime, + serializer: S, +) -> Result<S::Ok, S::Error> { + datetime + .format(&Iso8601::<SERDE_CONFIG>) + .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<OffsetDateTime, D::Error> { + deserializer.deserialize_any(Visitor::<Iso8601<SERDE_CONFIG>>(PhantomData)) +} + +/// Use the well-known ISO 8601 format when serializing and deserializing an +/// [`Option<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 +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an [`Option<OffsetDateTime>`] using the well-known ISO 8601 format. + #[cfg(feature = "formatting")] + pub fn serialize<S: Serializer>( + option: &Option<OffsetDateTime>, + serializer: S, + ) -> Result<S::Ok, S::Error> { + option + .map(|odt| odt.format(&Iso8601::<SERDE_CONFIG>)) + .transpose() + .map_err(S::Error::custom)? + .serialize(serializer) + } + + /// Deserialize an [`Option<OffsetDateTime>`] from its ISO 8601 representation. + #[cfg(feature = "parsing")] + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + deserializer.deserialize_option(Visitor::<Option<Iso8601<SERDE_CONFIG>>>(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(|| <A::Error as serde::de::Error>::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, "<format string>")`. 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<Date>`. 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<OffsetDateTime>, +/// } +/// ``` +/// +/// [`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<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[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<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(2, Visitor::<Self>(PhantomData)) + } + } +} +// endregion date + +// region: Duration +impl Serialize for Duration { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[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<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(2, Visitor::<Self>(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<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[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<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(9, Visitor::<Self>(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<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[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(<S::Error>::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<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(6, Visitor::<Self>(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(<modifier::Hour>::default())), + FormatItem::Literal(b":"), + FormatItem::Component(Component::Minute(<modifier::Minute>::default())), + FormatItem::Literal(b":"), + FormatItem::Component(Component::Second(<modifier::Second>::default())), + FormatItem::Literal(b"."), + FormatItem::Component(Component::Subsecond(<modifier::Subsecond>::default())), +]; + +impl Serialize for Time { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[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<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(4, Visitor::<Self>(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<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[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<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(3, Visitor::<Self>(PhantomData)) + } + } +} +// endregion UtcOffset + +// region: Weekday +impl Serialize for Weekday { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[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<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_u8(Visitor::<Self>(PhantomData)) + } + } +} +// endregion Weekday + +// region: Month +impl Serialize for Month { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[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<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_u8(Visitor::<Self>(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<S: Serializer>( + datetime: &OffsetDateTime, + serializer: S, +) -> Result<S::Ok, S::Error> { + 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<OffsetDateTime, D::Error> { + deserializer.deserialize_str(Visitor::<Rfc2822>(PhantomData)) +} + +/// Use the well-known [RFC2822 format] when serializing and deserializing an +/// [`Option<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 +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an [`Option<OffsetDateTime>`] using the well-known RFC2822 format. + #[cfg(feature = "formatting")] + pub fn serialize<S: Serializer>( + option: &Option<OffsetDateTime>, + serializer: S, + ) -> Result<S::Ok, S::Error> { + option + .map(|odt| odt.format(&Rfc2822)) + .transpose() + .map_err(S::Error::custom)? + .serialize(serializer) + } + + /// Deserialize an [`Option<OffsetDateTime>`] from its RFC2822 representation. + #[cfg(feature = "parsing")] + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + deserializer.deserialize_option(Visitor::<Option<Rfc2822>>(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<S: Serializer>( + datetime: &OffsetDateTime, + serializer: S, +) -> Result<S::Ok, S::Error> { + 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<OffsetDateTime, D::Error> { + deserializer.deserialize_str(Visitor::<Rfc3339>(PhantomData)) +} + +/// Use the well-known [RFC3339 format] when serializing and deserializing an +/// [`Option<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 +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an [`Option<OffsetDateTime>`] using the well-known RFC3339 format. + #[cfg(feature = "formatting")] + pub fn serialize<S: Serializer>( + option: &Option<OffsetDateTime>, + serializer: S, + ) -> Result<S::Ok, S::Error> { + option + .map(|odt| odt.format(&Rfc3339)) + .transpose() + .map_err(S::Error::custom)? + .serialize(serializer) + } + + /// Deserialize an [`Option<OffsetDateTime>`] from its RFC3339 representation. + #[cfg(feature = "parsing")] + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + deserializer.deserialize_option(Visitor::<Option<Rfc3339>>(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<S: Serializer>( + datetime: &OffsetDateTime, + serializer: S, +) -> Result<S::Ok, S::Error> { + datetime.unix_timestamp().serialize(serializer) +} + +/// Deserialize an `OffsetDateTime` from its Unix timestamp +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> { + OffsetDateTime::from_unix_timestamp(<_>::deserialize(deserializer)?) + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) +} + +/// Treat an `Option<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 +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an `Option<OffsetDateTime>` as its Unix timestamp + pub fn serialize<S: Serializer>( + option: &Option<OffsetDateTime>, + serializer: S, + ) -> Result<S::Ok, S::Error> { + option + .map(OffsetDateTime::unix_timestamp) + .serialize(serializer) + } + + /// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result<Option<OffsetDateTime>, 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<T: ?Sized>(pub(super) PhantomData<T>); + +impl<'a> de::Visitor<'a> for Visitor<Date> { + type Value = Date; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `Date`") + } + + #[cfg(feature = "parsing")] + fn visit_str<E: de::Error>(self, value: &str) -> Result<Date, E> { + Date::parse(value, &DATE_FORMAT).map_err(E::custom) + } + + fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<Date, A::Error> { + 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<Duration> { + type Value = Duration; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `Duration`") + } + + fn visit_str<E: de::Error>(self, value: &str) -> Result<Duration, E> { + 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<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<Duration, A::Error> { + let seconds = item!(seq, "seconds")?; + let nanoseconds = item!(seq, "nanoseconds")?; + Ok(Duration::new(seconds, nanoseconds)) + } +} + +impl<'a> de::Visitor<'a> for Visitor<OffsetDateTime> { + type Value = OffsetDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("an `OffsetDateTime`") + } + + #[cfg(feature = "parsing")] + fn visit_str<E: de::Error>(self, value: &str) -> Result<OffsetDateTime, E> { + OffsetDateTime::parse(value, &OFFSET_DATE_TIME_FORMAT).map_err(E::custom) + } + + fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<OffsetDateTime, A::Error> { + 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<PrimitiveDateTime> { + type Value = PrimitiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `PrimitiveDateTime`") + } + + #[cfg(feature = "parsing")] + fn visit_str<E: de::Error>(self, value: &str) -> Result<PrimitiveDateTime, E> { + PrimitiveDateTime::parse(value, &PRIMITIVE_DATE_TIME_FORMAT).map_err(E::custom) + } + + fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<PrimitiveDateTime, A::Error> { + 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<Time> { + type Value = Time; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `Time`") + } + + #[cfg(feature = "parsing")] + fn visit_str<E: de::Error>(self, value: &str) -> Result<Time, E> { + Time::parse(value, &TIME_FORMAT).map_err(E::custom) + } + + fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<Time, A::Error> { + let hour = item!(seq, "hour")?; + let minute = item!(seq, "minute")?; + let second = item!(seq, "second")?; + let nanosecond = item!(seq, "nanosecond")?; + + Time::from_hms_nano(hour, minute, second, nanosecond).map_err(ComponentRange::into_de_error) + } +} + +impl<'a> de::Visitor<'a> for Visitor<UtcOffset> { + type Value = UtcOffset; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `UtcOffset`") + } + + #[cfg(feature = "parsing")] + fn visit_str<E: de::Error>(self, value: &str) -> Result<UtcOffset, E> { + UtcOffset::parse(value, &UTC_OFFSET_FORMAT).map_err(E::custom) + } + + fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<UtcOffset, A::Error> { + let hours = item!(seq, "offset hours")?; + let minutes = item!(seq, "offset minutes")?; + let seconds = item!(seq, "offset seconds")?; + + UtcOffset::from_hms(hours, minutes, seconds).map_err(ComponentRange::into_de_error) + } +} + +impl<'a> de::Visitor<'a> for Visitor<Weekday> { + type Value = Weekday; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `Weekday`") + } + + fn visit_str<E: de::Error>(self, value: &str) -> Result<Weekday, E> { + match value { + "Monday" => Ok(Weekday::Monday), + "Tuesday" => Ok(Weekday::Tuesday), + "Wednesday" => Ok(Weekday::Wednesday), + "Thursday" => Ok(Weekday::Thursday), + "Friday" => Ok(Weekday::Friday), + "Saturday" => Ok(Weekday::Saturday), + "Sunday" => Ok(Weekday::Sunday), + _ => Err(E::invalid_value(de::Unexpected::Str(value), &"a `Weekday`")), + } + } + + fn visit_u64<E: de::Error>(self, value: u64) -> Result<Weekday, E> { + match value { + 1 => Ok(Weekday::Monday), + 2 => Ok(Weekday::Tuesday), + 3 => Ok(Weekday::Wednesday), + 4 => Ok(Weekday::Thursday), + 5 => Ok(Weekday::Friday), + 6 => Ok(Weekday::Saturday), + 7 => Ok(Weekday::Sunday), + _ => Err(E::invalid_value( + de::Unexpected::Unsigned(value), + &"a value in the range 1..=7", + )), + } + } +} + +impl<'a> de::Visitor<'a> for Visitor<Month> { + type Value = Month; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `Month`") + } + + fn visit_str<E: de::Error>(self, value: &str) -> Result<Month, E> { + match value { + "January" => Ok(Month::January), + "February" => Ok(Month::February), + "March" => Ok(Month::March), + "April" => Ok(Month::April), + "May" => Ok(Month::May), + "June" => Ok(Month::June), + "July" => Ok(Month::July), + "August" => Ok(Month::August), + "September" => Ok(Month::September), + "October" => Ok(Month::October), + "November" => Ok(Month::November), + "December" => Ok(Month::December), + _ => Err(E::invalid_value(de::Unexpected::Str(value), &"a `Month`")), + } + } + + fn visit_u64<E: de::Error>(self, value: u64) -> Result<Month, E> { + match value { + 1 => Ok(Month::January), + 2 => Ok(Month::February), + 3 => Ok(Month::March), + 4 => Ok(Month::April), + 5 => Ok(Month::May), + 6 => Ok(Month::June), + 7 => Ok(Month::July), + 8 => Ok(Month::August), + 9 => Ok(Month::September), + 10 => Ok(Month::October), + 11 => Ok(Month::November), + 12 => Ok(Month::December), + _ => Err(E::invalid_value( + de::Unexpected::Unsigned(value), + &"a value in the range 1..=12", + )), + } + } +} + +/// Implement a visitor for a well-known format. +macro_rules! well_known { + ($article:literal, $name:literal, $($ty:tt)+) => { + #[cfg(feature = "parsing")] + impl<'a> de::Visitor<'a> for Visitor<$($ty)+> { + type Value = OffsetDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(concat!($article, " ", $name, "-formatted `OffsetDateTime`")) + } + + fn visit_str<E: de::Error>(self, value: &str) -> Result<OffsetDateTime, E> { + OffsetDateTime::parse(value, &$($ty)+).map_err(E::custom) + } + } + + #[cfg(feature = "parsing")] + impl<'a> de::Visitor<'a> for Visitor<Option<$($ty)+>> { + type Value = Option<OffsetDateTime>; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(concat!( + $article, + " ", + $name, + "-formatted `Option<OffsetDateTime>`" + )) + } + + fn visit_some<D: Deserializer<'a>>( + self, + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + deserializer + .deserialize_any(Visitor::<$($ty)+>(PhantomData)) + .map(Some) + } + + fn visit_none<E: de::Error>(self) -> Result<Option<OffsetDateTime>, E> { + Ok(None) + } + + fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> { + Ok(None) + } + } + }; +} + +well_known!("an", "RFC2822", Rfc2822); +well_known!("an", "RFC3339", Rfc3339); +well_known!( + "an", + "ISO 8601", + Iso8601::<{ super::iso8601::SERDE_CONFIG }> +); |