From 0def7da5a8fbb2b31b4caada650e112ec73685fb Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Tue, 29 Jan 2019 20:29:14 +0700 Subject: [PATCH] Impl Ser/De for RangeFrom, RangeTo, RangeToInclusive --- serde/src/de/impls.rs | 144 ++++++++++++++++++++++++++++++++++- serde/src/lib.rs | 2 +- serde/src/ser/impls.rs | 51 +++++++++++++ test_suite/tests/test_de.rs | 24 ++++++ test_suite/tests/test_ser.rs | 41 ++++++++++ 5 files changed, 260 insertions(+), 2 deletions(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index 29148702..9da2b918 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -2138,6 +2138,72 @@ where } } +impl<'de, Idx> Deserialize<'de> for RangeFrom +where + Idx: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let field = range::Field::Start; + let start = deserializer.deserialize_struct( + "RangeFrom", + field.name_slice(), + range::UnboundedRangeVisitor { + expecting: "struct RangeFrom", + phantom: PhantomData, + field, + }, + )?; + Ok(start..) + } +} + +impl<'de, Idx> Deserialize<'de> for RangeTo +where + Idx: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let field = range::Field::End; + let end = deserializer.deserialize_struct( + "RangeTo", + field.name_slice(), + range::UnboundedRangeVisitor { + expecting: "struct RangeTo", + phantom: PhantomData, + field, + }, + )?; + Ok(..end) + } +} + +impl<'de, Idx> Deserialize<'de> for RangeToInclusive +where + Idx: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let field = range::Field::End; + let end = deserializer.deserialize_struct( + "RangeToInclusive", + field.name_slice(), + range::UnboundedRangeVisitor { + expecting: "struct RangeToInclusive", + phantom: PhantomData, + field, + }, + )?; + Ok(..=end) + } +} + mod range { use lib::*; @@ -2149,11 +2215,31 @@ mod range { // // #[derive(Deserialize)] // #[serde(field_identifier, rename_all = "lowercase")] - enum Field { + #[derive(PartialEq)] + pub enum Field { Start, End, } + const FIELDS_START_ONLY: &'static [&'static str] = &["start"]; + const FIELD_END_ONLY: &'static [&'static str] = &["end"]; + + impl Field { + fn name(&self) -> &'static str { + match self { + Field::Start => "start", + Field::End => "end", + } + } + + pub fn name_slice(&self) -> &'static [&'static str] { + match self { + Field::Start => FIELDS_START_ONLY, + Field::End => FIELD_END_ONLY, + } + } + } + impl<'de> Deserialize<'de> for Field { fn deserialize(deserializer: D) -> Result where @@ -2265,6 +2351,62 @@ mod range { Ok((start, end)) } } + + pub struct UnboundedRangeVisitor { + pub expecting: &'static str, + pub phantom: PhantomData, + pub field: Field, + } + + impl<'de, Idx> Visitor<'de> for UnboundedRangeVisitor + where + Idx: Deserialize<'de>, + { + type Value = Idx; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(self.expecting) + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let value: Idx = match try!(seq.next_element()) { + Some(value) => value, + None => { + return Err(Error::invalid_length(0, &self)); + } + }; + Ok(value) + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut value: Option = None; + while let Some(key) = try!(map.next_key()) { + let key: Field = key; + match key { + ref key if *key == self.field => { + if value.is_some() { + return Err(::duplicate_field(key.name())); + } + value = Some(try!(map.next_value())); + } + key => { + return Err(::unknown_field(key.name(), self.field.name_slice())); + } + } + } + let value = match value { + Some(value) => value, + None => return Err(::missing_field(self.field.name())), + }; + Ok(value) + } + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/serde/src/lib.rs b/serde/src/lib.rs index c2fe3ff2..07202fa6 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, Bound}; + pub use self::core::ops::{Range, Bound, RangeFrom, RangeTo, RangeToInclusive}; 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 af672418..c290889b 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -256,6 +256,57 @@ where //////////////////////////////////////////////////////////////////////////////// +impl Serialize for RangeFrom +where + Idx: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use super::SerializeStruct; + let mut state = try!(serializer.serialize_struct("RangeFrom", 1)); + try!(state.serialize_field("start", &self.start)); + state.end() + } +} + +//////////////////////////////////////////////////////////////////////////////// + +impl Serialize for RangeTo +where + Idx: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use super::SerializeStruct; + let mut state = try!(serializer.serialize_struct("RangeTo", 1)); + try!(state.serialize_field("end", &self.end)); + state.end() + } +} + +//////////////////////////////////////////////////////////////////////////////// + +impl Serialize for RangeToInclusive +where + Idx: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use super::SerializeStruct; + let mut state = try!(serializer.serialize_struct("RangeToInclusive", 1)); + try!(state.serialize_field("end", &self.end)); + state.end() + } +} + +//////////////////////////////////////////////////////////////////////////////// + impl Serialize for Bound where T: Serialize, diff --git a/test_suite/tests/test_de.rs b/test_suite/tests/test_de.rs index 461494f3..8a4b346d 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -837,6 +837,30 @@ declare_tests! { Token::SeqEnd, ], } + test_range_from { + 0u8.. => &[ + Token::Struct { name: "RangeFrom", len: 1 }, + Token::Str("start"), + Token::U8(0), + Token::StructEnd, + ], + } + test_range_to { + ..2u8 => &[ + Token::Struct { name: "RangeTo", len: 1 }, + Token::Str("end"), + Token::U8(2), + Token::StructEnd, + ], + } + test_range_to_inclusive { + ..=2u8 => &[ + Token::Struct { name: "RangeToInclusive", len: 1 }, + Token::Str("end"), + Token::U8(2), + Token::StructEnd, + ], + } test_bound { Bound::Unbounded::<()> => &[ Token::Enum { name: "Bound" }, diff --git a/test_suite/tests/test_ser.rs b/test_suite/tests/test_ser.rs index f96961ab..9a56e9eb 100644 --- a/test_suite/tests/test_ser.rs +++ b/test_suite/tests/test_ser.rs @@ -376,6 +376,47 @@ declare_tests! { Token::StructEnd, ], } + test_range_from { + 0u8.. => &[ + Token::Struct { name: "RangeFrom", len: 1 }, + Token::Str("start"), + Token::U8(0), + Token::StructEnd, + ], + } + test_range_to { + ..2u8 => &[ + Token::Struct { name: "RangeTo", len: 1 }, + Token::Str("end"), + Token::U8(2), + Token::StructEnd, + ], + } + test_range_to_inclusive { + ..=2u8 => &[ + Token::Struct { name: "RangeToInclusive", len: 1 }, + Token::Str("end"), + Token::U8(2), + Token::StructEnd, + ], + } + test_bound { + Bound::Unbounded::<()> => &[ + Token::Enum { name: "Bound" }, + Token::Str("Unbounded"), + Token::Unit, + ], + Bound::Included(0u8) => &[ + Token::Enum { name: "Bound" }, + Token::Str("Included"), + Token::U8(0), + ], + Bound::Excluded(0u8) => &[ + Token::Enum { name: "Bound" }, + Token::Str("Excluded"), + Token::U8(0), + ], + } test_path { Path::new("/usr/local/lib") => &[ Token::Str("/usr/local/lib"),