From 8b2e6baf78b4908075017313c10347a2285d3131 Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 5 Aug 2018 10:45:50 -0300 Subject: [PATCH 1/2] 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 3e21b948..ae02760d 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 84a659a2..0db9367d 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 cb273982..558cb74c 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 d1d4bd7f..60b9a3e3 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 3933232c..22570fbd 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 eff19069..086029b5 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"), From 8eb195edf01f46e5a0defd97c0a61a31d18942fe Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 5 Aug 2018 17:38:41 -0300 Subject: [PATCH 2/2] Fix tests --- serde/build.rs | 6 ++++++ serde/src/de/impls.rs | 12 ++++++------ serde/src/lib.rs | 3 +++ serde/src/ser/impls.rs | 4 ++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/serde/build.rs b/serde/build.rs index a187cf85..3e82ddda 100644 --- a/serde/build.rs +++ b/serde/build.rs @@ -36,6 +36,12 @@ fn main() { println!("cargo:rustc-cfg=integer128"); } + // Inclusive ranges methods stabilized in Rust 1.27: + // https://github.com/rust-lang/rust/pull/50758 + if minor >= 27 { + println!("cargo:rustc-cfg=range_inclusive"); + } + // Non-zero integers stabilized in Rust 1.28: // https://github.com/rust-lang/rust/pull/50808 if minor >= 28 { diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index ae02760d..39a8cf4e 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -2196,8 +2196,8 @@ where //////////////////////////////////////////////////////////////////////////////// -#[cfg(feature = "std")] -impl<'de, Idx> Deserialize<'de> for ops::RangeInclusive +#[cfg(range_inclusive)] +impl<'de, Idx> Deserialize<'de> for RangeInclusive where Idx: Deserialize<'de>, { @@ -2243,7 +2243,7 @@ where b"start" => Ok(Field::Start), b"end" => Ok(Field::End), _ => { - let value = String::from_utf8_lossy(value); + let value = ::export::from_utf8_lossy(value); Err(Error::unknown_field(&value, FIELDS)) } } @@ -2262,7 +2262,7 @@ where where Idx: Deserialize<'de>, { - type Value = ops::RangeInclusive; + type Value = RangeInclusive; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("struct RangeInclusive") @@ -2284,7 +2284,7 @@ where return Err(Error::invalid_length(1, &self)); } }; - Ok(start..=end) + Ok(RangeInclusive::new(start, end)) } fn visit_map(self, mut map: A) -> Result @@ -2317,7 +2317,7 @@ where Some(end) => end, None => return Err(::missing_field("end")), }; - Ok(start..=end) + Ok(RangeInclusive::new(start, end)) } } diff --git a/serde/src/lib.rs b/serde/src/lib.rs index ff6dbd3c..79fd5b7b 100644 --- a/serde/src/lib.rs +++ b/serde/src/lib.rs @@ -226,6 +226,9 @@ mod lib { #[cfg(any(core_duration, feature = "std"))] pub use self::core::time::Duration; + + #[cfg(range_inclusive)] + pub use self::core::ops::RangeInclusive; } //////////////////////////////////////////////////////////////////////////////// diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index 558cb74c..624d07a5 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -246,8 +246,8 @@ where //////////////////////////////////////////////////////////////////////////////// -#[cfg(feature = "std")] -impl Serialize for ops::RangeInclusive +#[cfg(range_inclusive)] +impl Serialize for RangeInclusive where Idx: Serialize, {