From 5dd327fb025f4d9f78cd94853a48363a1bb30be0 Mon Sep 17 00:00:00 2001 From: Wangshan Lu Date: Sun, 4 Jun 2017 16:39:03 +0800 Subject: [PATCH 1/3] Support std::time::SystemTime --- serde/src/de/impls.rs | 126 +++++++++++++++++++++++++++++++++++++++++ serde/src/de/mod.rs | 1 + serde/src/lib.rs | 2 +- serde/src/ser/impls.rs | 17 ++++++ serde/src/ser/mod.rs | 1 + 5 files changed, 146 insertions(+), 1 deletion(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index 13f5c996..694498b3 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -1364,6 +1364,132 @@ impl<'de> Deserialize<'de> for Duration { //////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "std")] +impl<'de> Deserialize<'de> for SystemTime { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + // Reuse duration + enum Field { + Secs, + Nanos, + }; + + impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FieldVisitor; + + impl<'de> Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("`secs_since_epoch` or `nanos_since_epoch`") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match value { + "secs_since_epoch" => Ok(Field::Secs), + "nanos_since_epoch" => Ok(Field::Nanos), + _ => Err(Error::unknown_field(value, FIELDS)), + } + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + match value { + b"secs_since_epoch" => Ok(Field::Secs), + b"nanos_since_epoch" => Ok(Field::Nanos), + _ => { + let value = String::from_utf8_lossy(value); + Err(Error::unknown_field(&value, FIELDS)) + } + } + } + } + + deserializer.deserialize_identifier(FieldVisitor) + } + } + + struct DurationVisitor; + + impl<'de> Visitor<'de> for DurationVisitor { + type Value = Duration; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct Duration") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let secs: u64 = match try!(seq.next_element()) { + Some(value) => value, + None => { + return Err(Error::invalid_length(0, &self)); + } + }; + let nanos: u32 = match try!(seq.next_element()) { + Some(value) => value, + None => { + return Err(Error::invalid_length(1, &self)); + } + }; + Ok(Duration::new(secs, nanos)) + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut secs: Option = None; + let mut nanos: Option = None; + while let Some(key) = try!(map.next_key()) { + match key { + Field::Secs => { + if secs.is_some() { + return Err(::duplicate_field("secs_since_epoch")); + } + secs = Some(try!(map.next_value())); + } + Field::Nanos => { + if nanos.is_some() { + return Err(::duplicate_field("nanos_since_epoch")); + } + nanos = Some(try!(map.next_value())); + } + } + } + let secs = match secs { + Some(secs) => secs, + None => return Err(::missing_field("secs_since_epoch")), + }; + let nanos = match nanos { + Some(nanos) => nanos, + None => return Err(::missing_field("nanos_since_epoch")), + }; + Ok(Duration::new(secs, nanos)) + } + } + + const FIELDS: &'static [&'static str] = &["secs_since_epoch", "nanos_since_epoch"]; + let duration = try!(deserializer.deserialize_struct("Duration", FIELDS, DurationVisitor)); + Ok(UNIX_EPOCH + duration) + } +} + +//////////////////////////////////////////////////////////////////////////////// + // Similar to: // // #[derive(Deserialize)] diff --git a/serde/src/de/mod.rs b/serde/src/de/mod.rs index dd5f09da..cd16e462 100644 --- a/serde/src/de/mod.rs +++ b/serde/src/de/mod.rs @@ -94,6 +94,7 @@ //! - OsString //! - **Miscellaneous standard library types**: //! - Duration +//! - SystemTime //! - Path //! - PathBuf //! - Range\ diff --git a/serde/src/lib.rs b/serde/src/lib.rs index fe8258a9..fc2c3c27 100644 --- a/serde/src/lib.rs +++ b/serde/src/lib.rs @@ -187,7 +187,7 @@ mod lib { #[cfg(feature = "std")] pub use std::path::{Path, PathBuf}; #[cfg(feature = "std")] - pub use std::time::Duration; + pub use std::time::{Duration, SystemTime, UNIX_EPOCH}; #[cfg(feature = "std")] pub use std::sync::{Mutex, RwLock}; diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index 92f55c77..b26a8bd0 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -455,6 +455,23 @@ impl Serialize for Duration { //////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "std")] +impl Serialize for SystemTime { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use super::SerializeStruct; + let duration_since_epoch = self.duration_since(UNIX_EPOCH).expect("SystemTime must be later than UNIX_EPOCH"); + let mut state = try!(serializer.serialize_struct("SystemTime", 2)); + try!(state.serialize_field("secs_since_epoch", &duration_since_epoch.as_secs())); + try!(state.serialize_field("nanos_since_epoch", &duration_since_epoch.subsec_nanos())); + state.end() + } +} + +//////////////////////////////////////////////////////////////////////////////// + /// Serialize a value that implements `Display` as a string, when that string is /// statically known to never have more than a constant `MAX_LEN` bytes. /// diff --git a/serde/src/ser/mod.rs b/serde/src/ser/mod.rs index 13137b3b..87307bb2 100644 --- a/serde/src/ser/mod.rs +++ b/serde/src/ser/mod.rs @@ -89,6 +89,7 @@ //! - OsString //! - **Miscellaneous standard library types**: //! - Duration +//! - SystemTime //! - Path //! - PathBuf //! - Range\ From b504b08782e96193495c55c275d681d35a31c1e2 Mon Sep 17 00:00:00 2001 From: Wangshan Lu Date: Wed, 12 Jul 2017 12:01:29 +0800 Subject: [PATCH 2/3] Fix SystemTime serde name --- serde/src/de/impls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index 694498b3..1a48f803 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -1426,7 +1426,7 @@ impl<'de> Deserialize<'de> for SystemTime { type Value = Duration; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("struct Duration") + formatter.write_str("struct SystemTime") } fn visit_seq(self, mut seq: A) -> Result @@ -1483,7 +1483,7 @@ impl<'de> Deserialize<'de> for SystemTime { } const FIELDS: &'static [&'static str] = &["secs_since_epoch", "nanos_since_epoch"]; - let duration = try!(deserializer.deserialize_struct("Duration", FIELDS, DurationVisitor)); + let duration = try!(deserializer.deserialize_struct("SystemTime", FIELDS, DurationVisitor)); Ok(UNIX_EPOCH + duration) } } From 0084d82a5032ecfb0e7d9f94b18ed595e3711d7d Mon Sep 17 00:00:00 2001 From: Wangshan Lu Date: Wed, 12 Jul 2017 12:01:40 +0800 Subject: [PATCH 3/3] Add tests for SystemTime --- test_suite/tests/test_de.rs | 19 ++++++++++++++++++- test_suite/tests/test_ser.rs | 13 ++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/test_suite/tests/test_de.rs b/test_suite/tests/test_de.rs index d1bddfaf..b915fba7 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -14,7 +14,7 @@ extern crate serde_derive; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::net; use std::path::{Path, PathBuf}; -use std::time::Duration; +use std::time::{Duration, UNIX_EPOCH}; use std::default::Default; use std::ffi::{CString, OsString}; @@ -682,6 +682,23 @@ declare_tests! { Token::SeqEnd, ], } + test_system_time { + UNIX_EPOCH + Duration::new(1, 2) => &[ + Token::Struct { name: "SystemTime", len: 2 }, + Token::Str("secs_since_epoch"), + Token::U64(1), + + Token::Str("nanos_since_epoch"), + Token::U32(2), + Token::StructEnd, + ], + UNIX_EPOCH + Duration::new(1, 2) => &[ + Token::Seq { len: Some(2) }, + Token::I64(1), + Token::I64(2), + Token::SeqEnd, + ], + } test_range { 1u32..2u32 => &[ Token::Struct { name: "Range", len: 2 }, diff --git a/test_suite/tests/test_ser.rs b/test_suite/tests/test_ser.rs index ae10b26b..35ed33c5 100644 --- a/test_suite/tests/test_ser.rs +++ b/test_suite/tests/test_ser.rs @@ -12,7 +12,7 @@ extern crate serde_derive; use std::collections::{BTreeMap, HashMap, HashSet}; use std::net; use std::path::{Path, PathBuf}; -use std::time::Duration; +use std::time::{Duration, UNIX_EPOCH}; use std::ffi::CString; #[cfg(unix)] @@ -319,6 +319,17 @@ declare_tests! { Token::StructEnd, ], } + test_system_time { + UNIX_EPOCH + Duration::new(1, 2) => &[ + Token::Struct { name: "SystemTime", len: 2 }, + Token::Str("secs_since_epoch"), + Token::U64(1), + + Token::Str("nanos_since_epoch"), + Token::U32(2), + Token::StructEnd, + ], + } test_range { 1u32..2u32 => &[ Token::Struct { name: "Range", len: 2 },