diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index 95531661..29148702 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -2269,6 +2269,116 @@ mod range { //////////////////////////////////////////////////////////////////////////////// +impl<'de, T> Deserialize<'de> for Bound +where + T: Deserialize<'de> +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + enum Field { + Unbounded, + Included, + Excluded, + } + + impl<'de> Deserialize<'de> for Field { + #[inline] + 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("`Unbounded`, `Included` or `Excluded`") + } + + fn visit_u32(self, value: u32) -> Result + where + E: Error, + { + match value { + 0 => Ok(Field::Unbounded), + 1 => Ok(Field::Included), + 2 => Ok(Field::Excluded), + _ => Err(Error::invalid_value( + Unexpected::Unsigned(value as u64), + &self, + )), + } + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match value { + "Unbounded" => Ok(Field::Unbounded), + "Included" => Ok(Field::Included), + "Excluded" => Ok(Field::Excluded), + _ => Err(Error::unknown_variant(value, VARIANTS)), + } + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + match value { + b"Unbounded" => Ok(Field::Unbounded), + b"Included" => Ok(Field::Included), + b"Excluded" => Ok(Field::Excluded), + _ => match str::from_utf8(value) { + Ok(value) => Err(Error::unknown_variant(value, VARIANTS)), + Err(_) => { + Err(Error::invalid_value(Unexpected::Bytes(value), &self)) + } + }, + } + } + } + + deserializer.deserialize_identifier(FieldVisitor) + } + } + + struct BoundVisitor(PhantomData>); + + impl<'de, T> Visitor<'de> for BoundVisitor + where + T: Deserialize<'de>, + { + type Value = Bound; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("enum Bound") + } + + fn visit_enum(self, data: A) -> Result + where + A: EnumAccess<'de>, + { + match try!(data.variant()) { + (Field::Unbounded, v) => v.newtype_variant().map(|_: T| Bound::Unbounded), + (Field::Included, v) => v.newtype_variant().map(Bound::Included), + (Field::Excluded, v) => v.newtype_variant().map(Bound::Excluded), + } + } + } + + const VARIANTS: &'static [&'static str] = &["Unbounded", "Included", "Excluded"]; + + deserializer.deserialize_enum("Bound", VARIANTS, BoundVisitor(PhantomData)) + } +} + +//////////////////////////////////////////////////////////////////////////////// + macro_rules! nonzero_integers { ( $( $T: ident, )+ ) => { $( diff --git a/serde/src/lib.rs b/serde/src/lib.rs index 9074899b..c2fe3ff2 100644 --- a/serde/src/lib.rs +++ b/serde/src/lib.rs @@ -160,7 +160,7 @@ mod lib { pub use self::core::default::{self, Default}; pub use self::core::fmt::{self, Debug, Display}; pub use self::core::marker::{self, PhantomData}; - pub use self::core::ops::Range; + pub use self::core::ops::{Range, Bound}; pub use self::core::option::{self, Option}; pub use self::core::result::{self, Result}; diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index 3fab473d..af672418 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -256,6 +256,24 @@ where //////////////////////////////////////////////////////////////////////////////// +impl Serialize for Bound +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Bound::Unbounded => serializer.serialize_unit_variant("Bound", 0, "Unbounded"), + Bound::Included(ref value) => serializer.serialize_newtype_variant("Bound", 1, "Included", value), + Bound::Excluded(ref value) => serializer.serialize_newtype_variant("Bound", 2, "Excluded", value), + } + } +} + +//////////////////////////////////////////////////////////////////////////////// + impl Serialize for () { #[inline] fn serialize(&self, serializer: S) -> Result diff --git a/test_suite/tests/test_de.rs b/test_suite/tests/test_de.rs index 5836d547..461494f3 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -10,6 +10,7 @@ use std::path::{Path, PathBuf}; use std::rc::{Rc, Weak as RcWeak}; use std::sync::{Arc, Weak as ArcWeak}; use std::time::{Duration, UNIX_EPOCH}; +use std::ops::Bound; use fnv::FnvHasher; use serde::{Deserialize, Deserializer}; @@ -836,6 +837,23 @@ declare_tests! { Token::SeqEnd, ], } + test_bound { + Bound::Unbounded::<()> => &[ + Token::Enum { name: "Bound" }, + Token::Str("Unbounded"), + Token::Unit, + ], + Bound::Included(0) => &[ + Token::Enum { name: "Bound" }, + Token::Str("Included"), + Token::U8(0), + ], + Bound::Excluded(0) => &[ + Token::Enum { name: "Bound" }, + Token::Str("Excluded"), + Token::U8(0), + ], + } test_path { Path::new("/usr/local/lib") => &[ Token::BorrowedStr("/usr/local/lib"), diff --git a/test_suite/tests/test_ser.rs b/test_suite/tests/test_ser.rs index b7bde4e1..f96961ab 100644 --- a/test_suite/tests/test_ser.rs +++ b/test_suite/tests/test_ser.rs @@ -10,6 +10,7 @@ use std::path::{Path, PathBuf}; use std::rc::{Rc, Weak as RcWeak}; use std::sync::{Arc, Weak as ArcWeak}; use std::time::{Duration, UNIX_EPOCH}; +use std::ops::Bound; #[cfg(unix)] use std::str;