319 lines
11 KiB
Rust
319 lines
11 KiB
Rust
//! 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(feature = "serde-well-known")]
|
|
pub mod rfc2822;
|
|
#[cfg(feature = "serde-well-known")]
|
|
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.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use time::OffsetDateTime;
|
|
/// # use ::serde::{Serialize, Deserialize};
|
|
/// use time::serde;
|
|
///
|
|
/// // Makes a module `mod my_format { ... }`.
|
|
/// serde::format_description!(my_format, OffsetDateTime, "hour=[hour], minute=[minute]");
|
|
///
|
|
/// #[derive(Serialize, Deserialize)]
|
|
/// struct SerializesWithCustom {
|
|
/// #[serde(with = "my_format")]
|
|
/// dt: OffsetDateTime,
|
|
/// #[serde(with = "my_format::option")]
|
|
/// maybe_dt: Option<OffsetDateTime>,
|
|
/// }
|
|
/// #
|
|
/// # // otherwise rustdoc tests don't work because we put a module in `main()`
|
|
/// # fn main() {}
|
|
/// ```
|
|
///
|
|
/// [`format_description::parse()`]: crate::format_description::parse()
|
|
#[cfg(all(feature = "macros", feature = "serde-human-readable"))]
|
|
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> {
|
|
deserializer.deserialize_any(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> {
|
|
deserializer.deserialize_any(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> {
|
|
deserializer.deserialize_any(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> {
|
|
deserializer.deserialize_any(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> {
|
|
deserializer.deserialize_any(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> {
|
|
deserializer.deserialize_any(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> {
|
|
deserializer.deserialize_any(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> {
|
|
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
|
|
}
|
|
}
|
|
// endregion Month
|