diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index e2319d17..0c33ec5f 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -53,6 +53,9 @@ use alloc::arc::Arc; #[cfg(all(feature = "unstable", feature = "alloc", not(feature = "std")))] use alloc::boxed::Box; +#[cfg(feature = "std")] +use std::time::Duration; + #[cfg(feature = "unstable")] use core::nonzero::{NonZero, Zeroable}; @@ -982,6 +985,135 @@ impl<'a, T: ?Sized> Deserialize for Cow<'a, T> where T: ToOwned, T::Owned: Deser /////////////////////////////////////////////////////////////////////////////// +// This is a cleaned-up version of the impl generated by: +// +// #[derive(Deserialize)] +// #[serde(deny_unknown_fields)] +// struct Duration { +// secs: u64, +// nanos: u32, +// } +#[cfg(feature = "std")] +impl Deserialize for Duration { + fn deserialize(deserializer: &mut D) -> Result + where D: Deserializer, + { + enum Field { Secs, Nanos }; + + impl Deserialize for Field { + fn deserialize(deserializer: &mut D) -> Result + where D: Deserializer, + { + struct FieldVisitor; + + impl Visitor for FieldVisitor { + type Value = Field; + + fn visit_usize(&mut self, value: usize) -> Result + where E: Error, + { + match value { + 0usize => Ok(Field::Secs), + 1usize => Ok(Field::Nanos), + _ => Err(Error::invalid_value("expected a field")), + } + } + + fn visit_str(&mut self, value: &str) -> Result + where E: Error, + { + match value { + "secs" => Ok(Field::Secs), + "nanos" => Ok(Field::Nanos), + _ => Err(Error::unknown_field(value)), + } + } + + fn visit_bytes(&mut self, value: &[u8]) -> Result + where E: Error, + { + match value { + b"secs" => Ok(Field::Secs), + b"nanos" => Ok(Field::Nanos), + _ => { + let value = String::from_utf8_lossy(value); + Err(Error::unknown_field(&value)) + } + } + } + } + + deserializer.deserialize_struct_field(FieldVisitor) + } + } + + struct DurationVisitor; + + impl Visitor for DurationVisitor { + type Value = Duration; + + fn visit_seq(&mut self, mut visitor: V) -> Result + where V: SeqVisitor, + { + let secs: u64 = match try!(visitor.visit()) { + Some(value) => value, + None => { + try!(visitor.end()); + return Err(Error::invalid_length(0)); + } + }; + let nanos: u32 = match try!(visitor.visit()) { + Some(value) => value, + None => { + try!(visitor.end()); + return Err(Error::invalid_length(1)); + } + }; + try!(visitor.end()); + Ok(Duration::new(secs, nanos)) + } + + fn visit_map(&mut self, mut visitor: V) -> Result + where V: MapVisitor, + { + let mut secs: Option = None; + let mut nanos: Option = None; + while let Some(key) = try!(visitor.visit_key::()) { + match key { + Field::Secs => { + if secs.is_some() { + return Err(::duplicate_field("secs")); + } + secs = Some(try!(visitor.visit_value())); + } + Field::Nanos => { + if nanos.is_some() { + return Err(::duplicate_field("nanos")); + } + nanos = Some(try!(visitor.visit_value())); + } + } + } + try!(visitor.end()); + let secs = match secs { + Some(secs) => secs, + None => try!(visitor.missing_field("secs")), + }; + let nanos = match nanos { + Some(nanos) => nanos, + None => try!(visitor.missing_field("nanos")), + }; + Ok(Duration::new(secs, nanos)) + } + } + + const FIELDS: &'static [&'static str] = &["secs", "nanos"]; + deserializer.deserialize_struct("Duration", FIELDS, DurationVisitor) + } +} + +/////////////////////////////////////////////////////////////////////////////// + #[cfg(feature = "unstable")] impl Deserialize for NonZero where T: Deserialize + PartialEq + Zeroable + Zero { fn deserialize(deserializer: &mut D) -> Result, D::Error> where D: Deserializer { diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index 106b1b50..348c8c77 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -50,6 +50,8 @@ use std::path; use std::rc::Rc; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::rc::Rc; +#[cfg(feature = "std")] +use std::time::Duration; #[cfg(feature = "std")] use std::sync::Arc; @@ -624,6 +626,20 @@ impl Serialize for Result where T: Serialize, E: Serialize { /////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "std")] +impl Serialize for Duration { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: Serializer, + { + let mut state = try!(serializer.serialize_struct("Duration", 2)); + try!(serializer.serialize_struct_elt(&mut state, "secs", self.as_secs())); + try!(serializer.serialize_struct_elt(&mut state, "nanos", self.subsec_nanos())); + serializer.serialize_struct_end(state) + } +} + +/////////////////////////////////////////////////////////////////////////////// + #[cfg(all(feature = "std", feature = "unstable"))] impl Serialize for net::IpAddr { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> diff --git a/testing/tests/test_de.rs b/testing/tests/test_de.rs index 63b6b6f7..162bf016 100644 --- a/testing/tests/test_de.rs +++ b/testing/tests/test_de.rs @@ -1,6 +1,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::net; use std::path::PathBuf; +use std::time::Duration; use serde::Deserialize; @@ -745,6 +746,28 @@ declare_tests! { Token::SeqEnd, ], } + test_duration { + Duration::new(1, 2) => &[ + Token::StructStart("Duration", 2), + Token::StructSep, + Token::Str("secs"), + Token::U64(1), + + Token::StructSep, + Token::Str("nanos"), + Token::U32(2), + Token::StructEnd, + ], + Duration::new(1, 2) => &[ + Token::SeqStart(Some(2)), + Token::SeqSep, + Token::I64(1), + + Token::SeqSep, + Token::I64(2), + Token::SeqEnd, + ], + } test_net_ipv4addr { "1.2.3.4".parse::().unwrap() => &[Token::Str("1.2.3.4")], } diff --git a/testing/tests/test_ser.rs b/testing/tests/test_ser.rs index 5fc7bf12..52573533 100644 --- a/testing/tests/test_ser.rs +++ b/testing/tests/test_ser.rs @@ -2,6 +2,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use std::net; use std::path::{Path, PathBuf}; use std::str; +use std::time::Duration; extern crate serde_test; use self::serde_test::{ @@ -324,6 +325,19 @@ declare_ser_tests! { Token::SeqEnd, ], } + test_duration { + Duration::new(1, 2) => &[ + Token::StructStart("Duration", 2), + Token::StructSep, + Token::Str("secs"), + Token::U64(1), + + Token::StructSep, + Token::Str("nanos"), + Token::U32(2), + Token::StructEnd, + ], + } test_net_ipv4addr { "1.2.3.4".parse::().unwrap() => &[Token::Str("1.2.3.4")], }