From 8b2e6baf78b4908075017313c10347a2285d3131 Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 5 Aug 2018 10:45:50 -0300 Subject: [PATCH] Implement Serialize and Deserialize for RangeInclusive --- serde/src/de/impls.rs | 138 +++++++++++++++++++++++++++++++++++ serde/src/de/mod.rs | 1 + serde/src/ser/impls.rs | 19 +++++ serde/src/ser/mod.rs | 1 + test_suite/tests/test_de.rs | 17 +++++ test_suite/tests/test_ser.rs | 11 +++ 6 files changed, 187 insertions(+) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index 3e21b948e..ae02760db 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -2196,6 +2196,144 @@ where //////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "std")] +impl<'de, Idx> Deserialize<'de> for ops::RangeInclusive +where + Idx: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + enum Field { + Start, + End, + }; + + 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("`start` or `end`") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match value { + "start" => Ok(Field::Start), + "end" => Ok(Field::End), + _ => Err(Error::unknown_field(value, FIELDS)), + } + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + match value { + b"start" => Ok(Field::Start), + b"end" => Ok(Field::End), + _ => { + let value = String::from_utf8_lossy(value); + Err(Error::unknown_field(&value, FIELDS)) + } + } + } + } + + deserializer.deserialize_identifier(FieldVisitor) + } + } + + struct RangeInclusiveVisitor { + phantom: PhantomData, + } + + impl<'de, Idx> Visitor<'de> for RangeInclusiveVisitor + where + Idx: Deserialize<'de>, + { + type Value = ops::RangeInclusive; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct RangeInclusive") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let start: Idx = match try!(seq.next_element()) { + Some(value) => value, + None => { + return Err(Error::invalid_length(0, &self)); + } + }; + let end: Idx = match try!(seq.next_element()) { + Some(value) => value, + None => { + return Err(Error::invalid_length(1, &self)); + } + }; + Ok(start..=end) + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut start: Option = None; + let mut end: Option = None; + while let Some(key) = try!(map.next_key()) { + match key { + Field::Start => { + if start.is_some() { + return Err(::duplicate_field("start")); + } + start = Some(try!(map.next_value())); + } + Field::End => { + if end.is_some() { + return Err(::duplicate_field("end")); + } + end = Some(try!(map.next_value())); + } + } + } + let start = match start { + Some(start) => start, + None => return Err(::missing_field("start")), + }; + let end = match end { + Some(end) => end, + None => return Err(::missing_field("end")), + }; + Ok(start..=end) + } + } + + const FIELDS: &'static [&'static str] = &["start", "end"]; + deserializer.deserialize_struct( + "RangeInclusive", + FIELDS, + RangeInclusiveVisitor { + phantom: PhantomData, + }, + ) + } +} + +//////////////////////////////////////////////////////////////////////////////// + macro_rules! nonzero_integers { ( $( $T: ident, )+ ) => { $( diff --git a/serde/src/de/mod.rs b/serde/src/de/mod.rs index 84a659a23..0db9367dc 100644 --- a/serde/src/de/mod.rs +++ b/serde/src/de/mod.rs @@ -97,6 +97,7 @@ //! - Path //! - PathBuf //! - Range\ +//! - RangeInclusive\ //! - num::NonZero* //! - `!` *(unstable)* //! - **Net types**: diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index cb2739820..558cb74c7 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -246,6 +246,25 @@ where //////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "std")] +impl Serialize for ops::RangeInclusive +where + Idx: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use super::SerializeStruct; + let mut state = try!(serializer.serialize_struct("RangeInclusive", 2)); + try!(state.serialize_field("start", &self.start())); + try!(state.serialize_field("end", &self.end())); + state.end() + } +} + +//////////////////////////////////////////////////////////////////////////////// + impl Serialize for () { #[inline] fn serialize(&self, serializer: S) -> Result diff --git a/serde/src/ser/mod.rs b/serde/src/ser/mod.rs index d1d4bd7fb..60b9a3e3e 100644 --- a/serde/src/ser/mod.rs +++ b/serde/src/ser/mod.rs @@ -92,6 +92,7 @@ //! - Path //! - PathBuf //! - Range\ +//! - RangeInclusive\ //! - num::NonZero* //! - `!` *(unstable)* //! - **Net types**: diff --git a/test_suite/tests/test_de.rs b/test_suite/tests/test_de.rs index 3933232ca..22570fbd4 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -811,6 +811,23 @@ declare_tests! { Token::SeqEnd, ], } + test_range_inclusive { + 1u32..=2u32 => &[ + Token::Struct { name: "RangeInclusive", len: 2 }, + Token::Str("start"), + Token::U32(1), + + Token::Str("end"), + Token::U32(2), + Token::StructEnd, + ], + 1u32..=2u32 => &[ + Token::Seq { len: Some(2) }, + Token::U64(1), + Token::U64(2), + Token::SeqEnd, + ], + } 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 eff19069c..086029b56 100644 --- a/test_suite/tests/test_ser.rs +++ b/test_suite/tests/test_ser.rs @@ -377,6 +377,17 @@ declare_tests! { Token::StructEnd, ], } + test_range_inclusive { + 1u32..=2u32 => &[ + Token::Struct { name: "RangeInclusive", len: 2 }, + Token::Str("start"), + Token::U32(1), + + Token::Str("end"), + Token::U32(2), + Token::StructEnd, + ], + } test_path { Path::new("/usr/local/lib") => &[ Token::Str("/usr/local/lib"),