Skip to content

Commit

Permalink
feat(datetime): impl Serialize/Deserialize for Date/Time
Browse files Browse the repository at this point in the history
  • Loading branch information
loqusion authored and epage committed Jul 30, 2024
1 parent fb83c29 commit c087867
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 0 deletions.
108 changes: 108 additions & 0 deletions crates/toml/tests/testsuite/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,114 @@ fn datetime_offset_issue_496() {
assert_data_eq!(output, original.raw());
}

#[test]
fn serialize_date() {
use toml::value::Date;

#[derive(Serialize)]
struct Document {
date: Date,
}

let input = Document {
date: Date {
year: 2024,
month: 1,
day: 1,
},
};
let raw = toml::to_string(&input).unwrap();
assert_data_eq!(
raw,
str![[r#"
date = 2024-01-01
"#]]
.raw()
);
}

#[test]
fn serialize_time() {
use toml::value::Time;

#[derive(Serialize)]
struct Document {
date: Time,
}

let input = Document {
date: Time {
hour: 5,
minute: 0,
second: 0,
nanosecond: 0,
},
};
let raw = toml::to_string(&input).unwrap();
assert_data_eq!(
raw,
str![[r#"
date = 05:00:00
"#]]
.raw()
);
}

#[test]
fn deserialize_date() {
use toml::value::Date;

#[derive(Debug, Deserialize)]
struct Document {
date: Date,
}

let document: Document = toml::from_str("date = 2024-01-01").unwrap();
assert_eq!(
document.date,
Date {
year: 2024,
month: 1,
day: 1
}
);

let err = toml::from_str::<Document>("date = 2024-01-01T05:00:00").unwrap_err();
assert_data_eq!(
err.message(),
str!["invalid type: local datetime, expected local date"]
);
}

#[test]
fn deserialize_time() {
use toml::value::Time;

#[derive(Debug, Deserialize)]
struct Document {
time: Time,
}

let document: Document = toml::from_str("time = 05:00:00").unwrap();
assert_eq!(
document.time,
Time {
hour: 5,
minute: 0,
second: 0,
nanosecond: 0,
}
);

let err = toml::from_str::<Document>("time = 2024-01-01T05:00:00").unwrap_err();
assert_data_eq!(
err.message(),
str!["invalid type: local datetime, expected local time"]
);
}

#[test]
fn serialize_array_with_none_value() {
#[derive(Serialize)]
Expand Down
91 changes: 91 additions & 0 deletions crates/toml_datetime/src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,37 @@ pub enum Offset {
},
}

impl Datetime {
#[cfg(feature = "serde")]
fn type_name(&self) -> &'static str {
match (
self.date.is_some(),
self.time.is_some(),
self.offset.is_some(),
) {
(true, true, true) => "offset datetime",
(true, true, false) => "local datetime",
(true, false, false) => Date::type_name(),
(false, true, false) => Time::type_name(),
_ => unreachable!("unsupported datetime combination"),
}
}
}

impl Date {
#[cfg(feature = "serde")]
fn type_name() -> &'static str {
"local date"
}
}

impl Time {
#[cfg(feature = "serde")]
fn type_name() -> &'static str {
"local time"
}
}

impl From<Date> for Datetime {
fn from(other: Date) -> Self {
Datetime {
Expand Down Expand Up @@ -480,6 +511,26 @@ impl ser::Serialize for Datetime {
}
}

#[cfg(feature = "serde")]
impl ser::Serialize for Date {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
Datetime::from(*self).serialize(serializer)
}
}

#[cfg(feature = "serde")]
impl ser::Serialize for Time {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
Datetime::from(*self).serialize(serializer)
}
}

#[cfg(feature = "serde")]
impl<'de> de::Deserialize<'de> for Datetime {
fn deserialize<D>(deserializer: D) -> Result<Datetime, D::Error>
Expand Down Expand Up @@ -513,6 +564,46 @@ impl<'de> de::Deserialize<'de> for Datetime {
}
}

#[cfg(feature = "serde")]
impl<'de> de::Deserialize<'de> for Date {
fn deserialize<D>(deserializer: D) -> Result<Date, D::Error>
where
D: de::Deserializer<'de>,
{
match Datetime::deserialize(deserializer)? {
Datetime {
date: Some(date),
time: None,
offset: None,
} => Ok(date),
datetime => Err(de::Error::invalid_type(
de::Unexpected::Other(&datetime.type_name()),

Check failure

Code scanning / clippy

this expression creates a reference which is immediately dereferenced by the compiler Error

this expression creates a reference which is immediately dereferenced by the compiler

Check failure

Code scanning / clippy

this expression creates a reference which is immediately dereferenced by the compiler Error

this expression creates a reference which is immediately dereferenced by the compiler
&Self::type_name(),
)),
}
}
}

#[cfg(feature = "serde")]
impl<'de> de::Deserialize<'de> for Time {
fn deserialize<D>(deserializer: D) -> Result<Time, D::Error>
where
D: de::Deserializer<'de>,
{
match Datetime::deserialize(deserializer)? {
Datetime {
date: None,
time: Some(time),
offset: None,
} => Ok(time),
datetime => Err(de::Error::invalid_type(
de::Unexpected::Other(&datetime.type_name()),

Check failure

Code scanning / clippy

this expression creates a reference which is immediately dereferenced by the compiler Error

this expression creates a reference which is immediately dereferenced by the compiler

Check failure

Code scanning / clippy

this expression creates a reference which is immediately dereferenced by the compiler Error

this expression creates a reference which is immediately dereferenced by the compiler
&Self::type_name(),
)),
}
}
}

#[cfg(feature = "serde")]
struct DatetimeKey;

Expand Down

0 comments on commit c087867

Please sign in to comment.