From 0dccbb1f11f1fb9f5b468e6a7b226bca90ba13cf Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Thu, 7 Sep 2017 15:56:22 +0200 Subject: [PATCH 01/10] Serialize to binary if the serde format is not human readable This implements the KISS suggested in https://github.com/serde-rs/serde/issues/790. It is possible that one of the other approaches may be better but this seemed like the simplest one to reignite som discussion. Personally I find the original suggestion of adding two traits perhaps slightly cleaner in theory but I think it ends up more complicated in the end since the added traits also need to be duplicated to to the `Seed` traits. Closes #790 --- serde/src/de/mod.rs | 6 ++++++ serde/src/ser/mod.rs | 6 ++++++ serde_test/src/assert.rs | 21 ++++++++++++++++++-- serde_test/src/de.rs | 11 ++++++++++- serde_test/src/lib.rs | 4 ++-- serde_test/src/ser.rs | 11 ++++++++++- test_suite/tests/test_de.rs | 38 +++++++++++++++++++++++++++++++++++- test_suite/tests/test_ser.rs | 26 +++++++++++++++++++++++- 8 files changed, 115 insertions(+), 8 deletions(-) diff --git a/serde/src/de/mod.rs b/serde/src/de/mod.rs index e90ced27..a590f4c6 100644 --- a/serde/src/de/mod.rs +++ b/serde/src/de/mod.rs @@ -1011,6 +1011,12 @@ pub trait Deserializer<'de>: Sized { fn deserialize_ignored_any(self, visitor: V) -> Result where V: Visitor<'de>; + + /// Returns whether the serialized data is human readable or not. + /// + /// Some formats are not intended to be human readable. For these formats + /// a type being serialized may opt to serialize into a more compact form. + fn is_human_readable(&self) -> bool { true } } //////////////////////////////////////////////////////////////////////////////// diff --git a/serde/src/ser/mod.rs b/serde/src/ser/mod.rs index 4356f930..fd7daf05 100644 --- a/serde/src/ser/mod.rs +++ b/serde/src/ser/mod.rs @@ -1363,6 +1363,12 @@ pub trait Serializer: Sized { fn collect_str(self, value: &T) -> Result where T: Display; + + /// Returns wheter the data format is human readable or not. + /// + /// Some formats are not intended to be human readable. For these formats + /// a type being serialized may opt to serialize into a more compact form. + fn is_human_readable(&self) -> bool { true } } /// Returned from `Serializer::serialize_seq`. diff --git a/serde_test/src/assert.rs b/serde_test/src/assert.rs index 2af2ebf5..31eb622f 100644 --- a/serde_test/src/assert.rs +++ b/serde_test/src/assert.rs @@ -84,7 +84,17 @@ pub fn assert_ser_tokens(value: &T, tokens: &[Token]) where T: Serialize, { - let mut ser = Serializer::new(tokens); + assert_ser_tokens_readable(value, tokens, true) +} + +/// Asserts that `value` serializes to the given `tokens`. +/// +/// See: `assert_ser_tokens` +pub fn assert_ser_tokens_readable(value: &T, tokens: &[Token], human_readable: bool) +where + T: Serialize, +{ + let mut ser = Serializer::readable(tokens, human_readable); match value.serialize(&mut ser) { Ok(_) => {} Err(err) => panic!("value failed to serialize: {}", err), @@ -183,7 +193,14 @@ pub fn assert_de_tokens<'de, T>(value: &T, tokens: &'de [Token]) where T: Deserialize<'de> + PartialEq + Debug, { - let mut de = Deserializer::new(tokens); + assert_de_tokens_readable(value, tokens, true) +} + +pub fn assert_de_tokens_readable<'de, T>(value: &T, tokens: &'de [Token], human_readable: bool) +where + T: Deserialize<'de> + PartialEq + Debug, +{ + let mut de = Deserializer::readable(tokens, human_readable); match T::deserialize(&mut de) { Ok(v) => assert_eq!(v, *value), Err(e) => panic!("tokens failed to deserialize: {}", e), diff --git a/serde_test/src/de.rs b/serde_test/src/de.rs index 9e465a73..4b0bc8ab 100644 --- a/serde_test/src/de.rs +++ b/serde_test/src/de.rs @@ -16,6 +16,7 @@ use token::Token; #[derive(Debug)] pub struct Deserializer<'de> { tokens: &'de [Token], + is_human_readable: bool, } macro_rules! assert_next_token { @@ -48,7 +49,11 @@ macro_rules! end_of_tokens { impl<'de> Deserializer<'de> { pub fn new(tokens: &'de [Token]) -> Self { - Deserializer { tokens: tokens } + Deserializer::readable(tokens, true) + } + + pub fn readable(tokens: &'de [Token], is_human_readable: bool) -> Self { + Deserializer { tokens: tokens, is_human_readable: is_human_readable } } fn peek_token_opt(&self) -> Option { @@ -364,6 +369,10 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { _ => self.deserialize_any(visitor), } } + + fn is_human_readable(&self) -> bool { + self.is_human_readable + } } ////////////////////////////////////////////////////////////////////////// diff --git a/serde_test/src/lib.rs b/serde_test/src/lib.rs index 391837d1..0ccf578a 100644 --- a/serde_test/src/lib.rs +++ b/serde_test/src/lib.rs @@ -168,8 +168,8 @@ mod token; mod assert; pub use token::Token; -pub use assert::{assert_tokens, assert_ser_tokens, assert_ser_tokens_error, assert_de_tokens, - assert_de_tokens_error}; +pub use assert::{assert_tokens, assert_ser_tokens, assert_ser_tokens_error, assert_ser_tokens_readable, + assert_de_tokens, assert_de_tokens_error, assert_de_tokens_readable}; // Not public API. #[doc(hidden)] diff --git a/serde_test/src/ser.rs b/serde_test/src/ser.rs index 31ba72ee..c7d64d29 100644 --- a/serde_test/src/ser.rs +++ b/serde_test/src/ser.rs @@ -15,12 +15,17 @@ use token::Token; #[derive(Debug)] pub struct Serializer<'a> { tokens: &'a [Token], + is_human_readable: bool, } impl<'a> Serializer<'a> { /// Creates the serializer. pub fn new(tokens: &'a [Token]) -> Self { - Serializer { tokens: tokens } + Serializer::readable(tokens, true) + } + + pub fn readable(tokens: &'a [Token], is_human_readable: bool) -> Self { + Serializer { tokens: tokens, is_human_readable: is_human_readable } } /// Pulls the next token off of the serializer, ignoring it. @@ -282,6 +287,10 @@ impl<'s, 'a> ser::Serializer for &'s mut Serializer<'a> { Ok(Variant { ser: self, end: Token::StructVariantEnd }) } } + + fn is_human_readable(&self) -> bool { + self.is_human_readable + } } pub struct Variant<'s, 'a: 's> { diff --git a/test_suite/tests/test_de.rs b/test_suite/tests/test_de.rs index 9fbfed46..e1a015e2 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -28,7 +28,7 @@ extern crate fnv; use self::fnv::FnvHasher; extern crate serde_test; -use self::serde_test::{Token, assert_de_tokens, assert_de_tokens_error}; +use self::serde_test::{Token, assert_de_tokens, assert_de_tokens_error, assert_de_tokens_readable}; #[macro_use] mod macros; @@ -1078,3 +1078,39 @@ declare_error_tests! { "invalid type: sequence, expected unit struct UnitStruct", } } + +#[derive(Debug, PartialEq)] +struct CompactBinary((u8, u8)); + +impl<'de> serde::Deserialize<'de> for CompactBinary { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + if deserializer.is_human_readable() { + <(u8, u8)>::deserialize(deserializer).map(CompactBinary) + } else { + <&[u8]>::deserialize(deserializer).map(|bytes| { + CompactBinary((bytes[0], bytes[1])) + }) + } + } +} + +#[test] +fn test_human_readable() { + assert_de_tokens( + &CompactBinary((1, 2)), + &[ + Token::Tuple { len: 2}, + Token::U8(1), + Token::U8(2), + Token::TupleEnd, + ], + ); + assert_de_tokens_readable( + &CompactBinary((1, 2)), + &[Token::BorrowedBytes(&[1, 2])], + false, + ); +} diff --git a/test_suite/tests/test_ser.rs b/test_suite/tests/test_ser.rs index 9dfb7053..a623e5d6 100644 --- a/test_suite/tests/test_ser.rs +++ b/test_suite/tests/test_ser.rs @@ -23,7 +23,8 @@ use std::str; extern crate serde; extern crate serde_test; -use self::serde_test::{Token, assert_ser_tokens, assert_ser_tokens_error}; +use self::serde_test::{Token, assert_ser_tokens, assert_ser_tokens_error, + assert_ser_tokens_readable}; extern crate fnv; use self::fnv::FnvHasher; @@ -474,3 +475,26 @@ fn test_enum_skipped() { "the enum variant Enum::SkippedMap cannot be serialized", ); } + +struct CompactBinary(String); + +impl serde::Serialize for CompactBinary { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer + { + if serializer.is_human_readable() { + serializer.serialize_str(&self.0) + } else { + serializer.serialize_bytes(self.0.as_bytes()) + } + } +} + +#[test] +fn test_human_readable() { + let value = CompactBinary("test".to_string()); + assert_ser_tokens(&value, &[Token::String("test")]); + + assert_ser_tokens_readable(&value, &[Token::Bytes(b"test")], false); +} From 40c670e625b540e957e2353e03daf8a13599f068 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 8 Sep 2017 10:37:33 +0200 Subject: [PATCH 02/10] Add non-human readable serializations for ip addresses --- serde/src/de/impls.rs | 95 +++++++++++++++++++++++++++++++++++++----- serde/src/ser/impls.rs | 40 ++++++++++++------ 2 files changed, 113 insertions(+), 22 deletions(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index ef3e20e6..be4a6301 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -868,38 +868,113 @@ map_impl!( //////////////////////////////////////////////////////////////////////////////// +macro_rules! count { + () => { + 0 + }; + ($first: expr $(,$rest: expr)*) => { + 1 + count!($($rest),*) + } +} + #[cfg(feature = "std")] -macro_rules! parse_impl { - ($ty:ty) => { +macro_rules! parse_ip_impl { + ($ty:ty; $($size: expr),*) => { impl<'de> Deserialize<'de> for $ty { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - let s = try!(String::deserialize(deserializer)); - s.parse().map_err(Error::custom) + struct ParseVisitor; + impl<'de> Visitor<'de> for ParseVisitor { + type Value = $ty; + + #[allow(unused_assignments)] + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "Expected bytes of length ")?; + let mut i = 0; + $( + let sep = match i { + 0 => "", + _ if i + 1 == count!($size) => " or ", + _ => ", ", + }; + write!(formatter, "{}{}", sep, $size)?; + i += 1; + )* + Ok(()) + } + + fn visit_str(self, value: &str) -> Result<$ty, E> + where + E: Error, + { + value.parse().map_err(Error::custom) + } + + fn visit_bytes(self, value: &[u8]) -> Result<$ty, E> + where + E: Error, + { + match value.len() { + $( + $size => { + let mut buffer = [0; $size]; + buffer.copy_from_slice(value); + Ok(<$ty>::from(buffer)) + } + )* + _ => Err(Error::invalid_length(value.len(), &self)), + } + } + } + if deserializer.is_human_readable() { + let s = try!(String::deserialize(deserializer)); + s.parse().map_err(Error::custom) + } else { + deserializer.deserialize_bytes(ParseVisitor) + } } } } } #[cfg(feature = "std")] -parse_impl!(net::IpAddr); +parse_ip_impl!(net::IpAddr; 16, 4); #[cfg(feature = "std")] -parse_impl!(net::Ipv4Addr); +parse_ip_impl!(net::Ipv4Addr; 4); #[cfg(feature = "std")] -parse_impl!(net::Ipv6Addr); +parse_ip_impl!(net::Ipv6Addr; 16); #[cfg(feature = "std")] -parse_impl!(net::SocketAddr); +macro_rules! parse_socket_impl { + ($ty:ty, $new: expr) => { + impl<'de> Deserialize<'de> for $ty { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + if deserializer.is_human_readable() { + let s = try!(String::deserialize(deserializer)); + s.parse().map_err(Error::custom) + } else { + <(_, u16)>::deserialize(deserializer).map(|(ip, port)| $new(ip, port)) + } + } + } + } +} #[cfg(feature = "std")] -parse_impl!(net::SocketAddrV4); +parse_socket_impl!(net::SocketAddr, net::SocketAddr::new); #[cfg(feature = "std")] -parse_impl!(net::SocketAddrV6); +parse_socket_impl!(net::SocketAddrV4, net::SocketAddrV4::new); + +#[cfg(feature = "std")] +parse_socket_impl!(net::SocketAddrV6, |ip, port| net::SocketAddrV6::new(ip, port, 0, 0)); //////////////////////////////////////////////////////////////////////////////// diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index ea59b78d..7fecd69f 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -519,9 +519,13 @@ impl Serialize for net::Ipv4Addr { where S: Serializer, { - /// "101.102.103.104".len() - const MAX_LEN: usize = 15; - serialize_display_bounded_length!(self, MAX_LEN, serializer) + if serializer.is_human_readable() { + /// "101.102.103.104".len() + const MAX_LEN: usize = 15; + serialize_display_bounded_length!(self, MAX_LEN, serializer) + } else { + self.octets().serialize(serializer) + } } } @@ -531,9 +535,13 @@ impl Serialize for net::Ipv6Addr { where S: Serializer, { - /// "1000:1002:1003:1004:1005:1006:1007:1008".len() - const MAX_LEN: usize = 39; - serialize_display_bounded_length!(self, MAX_LEN, serializer) + if serializer.is_human_readable() { + /// "1000:1002:1003:1004:1005:1006:1007:1008".len() + const MAX_LEN: usize = 39; + serialize_display_bounded_length!(self, MAX_LEN, serializer) + } else { + self.octets().serialize(serializer) + } } } @@ -556,9 +564,13 @@ impl Serialize for net::SocketAddrV4 { where S: Serializer, { - /// "101.102.103.104:65000".len() - const MAX_LEN: usize = 21; - serialize_display_bounded_length!(self, MAX_LEN, serializer) + if serializer.is_human_readable() { + /// "101.102.103.104:65000".len() + const MAX_LEN: usize = 21; + serialize_display_bounded_length!(self, MAX_LEN, serializer) + } else { + (self.ip().octets(), self.port()).serialize(serializer) + } } } @@ -568,9 +580,13 @@ impl Serialize for net::SocketAddrV6 { where S: Serializer, { - /// "[1000:1002:1003:1004:1005:1006:1007:1008]:65000".len() - const MAX_LEN: usize = 47; - serialize_display_bounded_length!(self, MAX_LEN, serializer) + if serializer.is_human_readable() { + /// "[1000:1002:1003:1004:1005:1006:1007:1008]:65000".len() + const MAX_LEN: usize = 47; + serialize_display_bounded_length!(self, MAX_LEN, serializer) + } else { + (self.ip().octets(), self.port()).serialize(serializer) + } } } From ad3335e5d6aac8db48773946def0720a0860aaad Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 11 Sep 2017 15:54:49 +0200 Subject: [PATCH 03/10] Serialize non-human-readble ip addresses as tuples Since we know exactly how many bytes we should serialize as we can hint to the serializer that it is not required which further reduces the serialized size when compared to just serializing as bytes. --- serde/src/de/impls.rs | 91 ++++++++++++++-------------------- serde/src/ser/impls.rs | 4 +- test_suite/tests/macros/mod.rs | 26 ++++++++++ test_suite/tests/test_de.rs | 89 ++++++++++++++++++++++++++++++--- test_suite/tests/test_ser.rs | 62 +++++++++++++++++++++++ 5 files changed, 207 insertions(+), 65 deletions(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index be4a6301..b20d9ac7 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -868,71 +868,19 @@ map_impl!( //////////////////////////////////////////////////////////////////////////////// -macro_rules! count { - () => { - 0 - }; - ($first: expr $(,$rest: expr)*) => { - 1 + count!($($rest),*) - } -} - #[cfg(feature = "std")] macro_rules! parse_ip_impl { - ($ty:ty; $($size: expr),*) => { + ($ty:ty; $size: expr) => { impl<'de> Deserialize<'de> for $ty { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - struct ParseVisitor; - impl<'de> Visitor<'de> for ParseVisitor { - type Value = $ty; - - #[allow(unused_assignments)] - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "Expected bytes of length ")?; - let mut i = 0; - $( - let sep = match i { - 0 => "", - _ if i + 1 == count!($size) => " or ", - _ => ", ", - }; - write!(formatter, "{}{}", sep, $size)?; - i += 1; - )* - Ok(()) - } - - fn visit_str(self, value: &str) -> Result<$ty, E> - where - E: Error, - { - value.parse().map_err(Error::custom) - } - - fn visit_bytes(self, value: &[u8]) -> Result<$ty, E> - where - E: Error, - { - match value.len() { - $( - $size => { - let mut buffer = [0; $size]; - buffer.copy_from_slice(value); - Ok(<$ty>::from(buffer)) - } - )* - _ => Err(Error::invalid_length(value.len(), &self)), - } - } - } if deserializer.is_human_readable() { let s = try!(String::deserialize(deserializer)); s.parse().map_err(Error::custom) } else { - deserializer.deserialize_bytes(ParseVisitor) + <[u8; $size]>::deserialize(deserializer).map(<$ty>::from) } } } @@ -940,7 +888,40 @@ macro_rules! parse_ip_impl { } #[cfg(feature = "std")] -parse_ip_impl!(net::IpAddr; 16, 4); +impl<'de> Deserialize<'de> for net::IpAddr { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + if deserializer.is_human_readable() { + let s = try!(String::deserialize(deserializer)); + s.parse().map_err(Error::custom) + } else { + struct EnumVisitor; + impl<'de> Visitor<'de> for EnumVisitor { + type Value = net::IpAddr; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a IpAddr") + } + + + fn visit_enum(self, data: A) -> Result + where + A: EnumAccess<'de>, + { + match try!(data.variant()) { + (0u32, v) => v.newtype_variant().map(net::IpAddr::V4), + (1u32, v) => v.newtype_variant().map(net::IpAddr::V6), + (_, _) => Err(Error::custom("Invalid IpAddr variant")), + } + } + } + const VARIANTS: &[&str] = &["V4", "V6"]; + deserializer.deserialize_enum("IpAddr", VARIANTS, EnumVisitor) + } + } +} #[cfg(feature = "std")] parse_ip_impl!(net::Ipv4Addr; 4); diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index 7fecd69f..014a6c52 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -569,7 +569,7 @@ impl Serialize for net::SocketAddrV4 { const MAX_LEN: usize = 21; serialize_display_bounded_length!(self, MAX_LEN, serializer) } else { - (self.ip().octets(), self.port()).serialize(serializer) + (self.ip(), self.port()).serialize(serializer) } } } @@ -585,7 +585,7 @@ impl Serialize for net::SocketAddrV6 { const MAX_LEN: usize = 47; serialize_display_bounded_length!(self, MAX_LEN, serializer) } else { - (self.ip().octets(), self.port()).serialize(serializer) + (self.ip(), self.port()).serialize(serializer) } } } diff --git a/test_suite/tests/macros/mod.rs b/test_suite/tests/macros/mod.rs index 4f6ea66e..9a917bcf 100644 --- a/test_suite/tests/macros/mod.rs +++ b/test_suite/tests/macros/mod.rs @@ -73,3 +73,29 @@ macro_rules! hashmap { } } } + +macro_rules! seq_impl { + (seq $first:expr,) => { + seq_impl!(seq $first) + }; + ($first:expr,) => { + seq_impl!($first) + }; + (seq $first:expr) => { + $first.into_iter() + }; + ($first:expr) => { + Some($first).into_iter() + }; + (seq $first:expr , $( $elem: tt)*) => { + $first.into_iter().chain(seq!( $($elem)* )) + }; + ($first:expr , $($elem: tt)*) => { + Some($first).into_iter().chain(seq!( $($elem)* )) + } +} +macro_rules! seq { + ($($tt: tt)*) => { + seq_impl!($($tt)*).collect::>() + }; +} diff --git a/test_suite/tests/test_de.rs b/test_suite/tests/test_de.rs index e1a015e2..ec0100bd 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -110,15 +110,15 @@ enum EnumSkipAll { ////////////////////////////////////////////////////////////////////////// macro_rules! declare_test { - ($name:ident { $($value:expr => $tokens:expr,)+ }) => { + ($name:ident $readable: ident { $($value:expr => $tokens:expr,)+ }) => { #[test] fn $name() { $( // Test ser/de roundtripping - assert_de_tokens(&$value, $tokens); + assert_de_tokens_readable(&$value, $tokens, $readable); // Test that the tokens are ignorable - assert_de_tokens_ignore($tokens); + assert_de_tokens_ignore($tokens, true); )+ } } @@ -127,7 +127,7 @@ macro_rules! declare_test { macro_rules! declare_tests { ($($name:ident { $($value:expr => $tokens:expr,)+ })+) => { $( - declare_test!($name { $($value => $tokens,)+ }); + declare_test!($name true { $($value => $tokens,)+ }); )+ } } @@ -143,7 +143,16 @@ macro_rules! declare_error_tests { } } -fn assert_de_tokens_ignore(ignorable_tokens: &[Token]) { +macro_rules! declare_non_human_readable_tests { + ($($name:ident { $($value:expr => $tokens:expr,)+ })+) => { + $( + declare_test!($name false { $($value => $tokens,)+ }); + )+ + } +} + + +fn assert_de_tokens_ignore(ignorable_tokens: &[Token], readable: bool) { #[derive(PartialEq, Debug, Deserialize)] struct IgnoreBase { a: i32, @@ -163,7 +172,7 @@ fn assert_de_tokens_ignore(ignorable_tokens: &[Token]) { .chain(vec![Token::MapEnd].into_iter()) .collect(); - let mut de = serde_test::Deserializer::new(&concated_tokens); + let mut de = serde_test::Deserializer::readable(&concated_tokens, readable); let base = IgnoreBase::deserialize(&mut de).unwrap(); assert_eq!(base, IgnoreBase { a: 1 }); } @@ -754,6 +763,70 @@ declare_tests! { } } +declare_non_human_readable_tests!{ + test_non_human_readable_net_ipv4addr { + net::Ipv4Addr::from(*b"1234") => &seq![ + Token::Tuple { len: 4 }, + seq b"1234".iter().map(|&b| Token::U8(b)), + Token::TupleEnd + ], + } + test_non_human_readable_net_ipv6addr { + net::Ipv6Addr::from(*b"1234567890123456") => &seq![ + Token::Tuple { len: 4 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd + ], + + } + test_non_human_readable_net_socketaddr { + net::SocketAddr::from((*b"1234567890123456", 1234)) => &seq![ + Token::Tuple { len: 2 }, + Token::Enum { name: "IpAddr" }, + Token::U32(1), + + Token::Tuple { len: 16 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd + ], + net::SocketAddr::from((*b"1234", 1234)) => &seq![ + Token::Tuple { len: 2 }, + Token::Enum { name: "IpAddr" }, + Token::U32(0), + + Token::Tuple { len: 4 }, + seq b"1234".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd + ], + net::SocketAddrV4::new(net::Ipv4Addr::from(*b"1234"), 1234) => &seq![ + Token::Tuple { len: 2 }, + + Token::Tuple { len: 4 }, + seq b"1234".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd + ], + net::SocketAddrV6::new(net::Ipv6Addr::from(*b"1234567890123456"), 1234, 0, 0) => &seq![ + Token::Tuple { len: 2 }, + + Token::Tuple { len: 16 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd + ], + } +} + #[cfg(feature = "unstable")] declare_tests! { test_rc_dst { @@ -795,7 +868,7 @@ fn test_osstring() { ]; assert_de_tokens(&value, &tokens); - assert_de_tokens_ignore(&tokens); + assert_de_tokens_ignore(&tokens, true); } #[cfg(windows)] @@ -815,7 +888,7 @@ fn test_osstring() { ]; assert_de_tokens(&value, &tokens); - assert_de_tokens_ignore(&tokens); + assert_de_tokens_ignore(&tokens, true); } #[cfg(feature = "unstable")] diff --git a/test_suite/tests/test_ser.rs b/test_suite/tests/test_ser.rs index a623e5d6..ce1ae654 100644 --- a/test_suite/tests/test_ser.rs +++ b/test_suite/tests/test_ser.rs @@ -78,6 +78,19 @@ macro_rules! declare_tests { } } +macro_rules! declare_non_human_readable_tests { + ($($name:ident { $($value:expr => $tokens:expr,)+ })+) => { + $( + #[test] + fn $name() { + $( + assert_ser_tokens_readable(&$value, $tokens, false); + )+ + } + )+ + } +} + declare_tests! { test_unit { () => &[Token::Unit], @@ -398,6 +411,55 @@ declare_tests! { } } +declare_non_human_readable_tests!{ + test_non_human_readable_net_ipv4addr { + net::Ipv4Addr::from(*b"1234") => &seq![ + Token::Tuple { len: 4 }, + seq b"1234".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + ], + } + test_non_human_readable_net_ipv6addr { + net::Ipv6Addr::from(*b"1234567890123456") => &seq![ + Token::Tuple { len: 16 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + ], + } + test_non_human_readable_net_socketaddr { + net::SocketAddr::from((*b"1234567890123456", 1234)) => &seq![ + Token::Tuple { len: 2 }, + + Token::Tuple { len: 16 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd, + ], + net::SocketAddrV4::new(net::Ipv4Addr::from(*b"1234"), 1234) => &seq![ + Token::Tuple { len: 2 }, + + Token::Tuple { len: 4 }, + seq b"1234".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd, + ], + net::SocketAddrV6::new(net::Ipv6Addr::from(*b"1234567890123456"), 1234, 0, 0) => &seq![ + Token::Tuple { len: 2 }, + + Token::Tuple { len: 16 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd, + ], + } +} + // Serde's implementation is not unstable, but the constructors are. #[cfg(feature = "unstable")] declare_tests! { From a52f436788f679afb938c7698a5e41dda9aea841 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 11 Sep 2017 16:03:00 +0200 Subject: [PATCH 04/10] Fix rustc 1.13 and clippy errors on travis --- serde/src/de/impls.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index b20d9ac7..70196573 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -911,13 +911,13 @@ impl<'de> Deserialize<'de> for net::IpAddr { A: EnumAccess<'de>, { match try!(data.variant()) { - (0u32, v) => v.newtype_variant().map(net::IpAddr::V4), - (1u32, v) => v.newtype_variant().map(net::IpAddr::V6), + (0_u32, v) => v.newtype_variant().map(net::IpAddr::V4), + (1_u32, v) => v.newtype_variant().map(net::IpAddr::V6), (_, _) => Err(Error::custom("Invalid IpAddr variant")), } } } - const VARIANTS: &[&str] = &["V4", "V6"]; + const VARIANTS: &'static [&'static str] = &["V4", "V6"]; deserializer.deserialize_enum("IpAddr", VARIANTS, EnumVisitor) } } From c2474bf6ee1a85f3084ad27df1ea740561d948e5 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 11 Sep 2017 17:18:35 +0200 Subject: [PATCH 05/10] Document that is_human_readable == false is a breaking change --- serde/src/de/mod.rs | 3 +++ serde/src/ser/impls.rs | 13 ++++++++++--- serde/src/ser/mod.rs | 3 +++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/serde/src/de/mod.rs b/serde/src/de/mod.rs index a590f4c6..71a707ef 100644 --- a/serde/src/de/mod.rs +++ b/serde/src/de/mod.rs @@ -1016,6 +1016,9 @@ pub trait Deserializer<'de>: Sized { /// /// Some formats are not intended to be human readable. For these formats /// a type being serialized may opt to serialize into a more compact form. + /// + /// NOTE: Implementing this method and returning `false` is considered a breaking + /// change as it may alter how any given type tries to deserialize itself. fn is_human_readable(&self) -> bool { true } } diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index 014a6c52..e513f98a 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -506,9 +506,16 @@ impl Serialize for net::IpAddr { where S: Serializer, { - match *self { - net::IpAddr::V4(ref a) => a.serialize(serializer), - net::IpAddr::V6(ref a) => a.serialize(serializer), + if serializer.is_human_readable() { + match *self { + net::IpAddr::V4(ref a) => a.serialize(serializer), + net::IpAddr::V6(ref a) => a.serialize(serializer), + } + } else { + match *self { + net::IpAddr::V4(ref a) => (0u8, a).serialize(serializer), + net::IpAddr::V6(ref a) => (1u8, a).serialize(serializer), + } } } } diff --git a/serde/src/ser/mod.rs b/serde/src/ser/mod.rs index fd7daf05..bbcceac7 100644 --- a/serde/src/ser/mod.rs +++ b/serde/src/ser/mod.rs @@ -1368,6 +1368,9 @@ pub trait Serializer: Sized { /// /// Some formats are not intended to be human readable. For these formats /// a type being serialized may opt to serialize into a more compact form. + /// + /// NOTE: Implementing this method and returning `false` is considered a breaking + /// change as it may alter how any given type tries to serialize itself. fn is_human_readable(&self) -> bool { true } } From 85c05d301a08d0b1e8e7f99ae0fd3305a40c2ba0 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 11 Sep 2017 17:39:57 +0200 Subject: [PATCH 06/10] Fix the non-readble IpAddr serialize implementations --- serde/src/ser/impls.rs | 21 ++++++++++++++++----- test_suite/tests/test_ser.rs | 11 +++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index e513f98a..3fa2ad9c 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -513,8 +513,10 @@ impl Serialize for net::IpAddr { } } else { match *self { - net::IpAddr::V4(ref a) => (0u8, a).serialize(serializer), - net::IpAddr::V6(ref a) => (1u8, a).serialize(serializer), + net::IpAddr::V4(ref a) => + serializer.serialize_newtype_variant("IpAddr", 0, "V4", a), + net::IpAddr::V6(ref a) => + serializer.serialize_newtype_variant("IpAddr", 1, "V6", a), } } } @@ -558,9 +560,18 @@ impl Serialize for net::SocketAddr { where S: Serializer, { - match *self { - net::SocketAddr::V4(ref addr) => addr.serialize(serializer), - net::SocketAddr::V6(ref addr) => addr.serialize(serializer), + if serializer.is_human_readable() { + match *self { + net::SocketAddr::V4(ref addr) => addr.serialize(serializer), + net::SocketAddr::V6(ref addr) => addr.serialize(serializer), + } + } else { + match *self { + net::SocketAddr::V4(ref addr) => + serializer.serialize_newtype_variant("SocketAddr", 0, "V4", addr), + net::SocketAddr::V6(ref addr) => + serializer.serialize_newtype_variant("SocketAddr", 1, "V6", addr), + } } } } diff --git a/test_suite/tests/test_ser.rs b/test_suite/tests/test_ser.rs index ce1ae654..3f3fa61c 100644 --- a/test_suite/tests/test_ser.rs +++ b/test_suite/tests/test_ser.rs @@ -426,8 +426,19 @@ declare_non_human_readable_tests!{ Token::TupleEnd, ], } + test_non_human_readable_net_ipaddr { + net::IpAddr::from(*b"1234") => &seq![ + Token::NewtypeVariant { name: "IpAddr", variant: "V4" }, + + Token::Tuple { len: 4 }, + seq b"1234".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + ], + } test_non_human_readable_net_socketaddr { net::SocketAddr::from((*b"1234567890123456", 1234)) => &seq![ + Token::NewtypeVariant { name: "SocketAddr", variant: "V6" }, + Token::Tuple { len: 2 }, Token::Tuple { len: 16 }, From e36915300f4008fceeee81d2dac8f7b922e91f4f Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Thu, 14 Sep 2017 16:54:47 +0200 Subject: [PATCH 07/10] Properly deserialize non-readable IpAddr and SocketAddr --- serde/src/de/impls.rs | 133 ++++++++++++++++++++++++----- serde_test/src/assert.rs | 14 ++- serde_test/src/lib.rs | 2 +- test_suite/tests/test_de.rs | 8 +- test_suite/tests/test_roundtrip.rs | 45 ++++++++++ 5 files changed, 173 insertions(+), 29 deletions(-) create mode 100644 test_suite/tests/test_roundtrip.rs diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index 70196573..9b68ebf7 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -887,6 +887,98 @@ macro_rules! parse_ip_impl { } } +macro_rules! deserialize_enum { + ($name: ident $name_kind: ident ( $($variant: ident; $bytes: expr; $index: expr),* ) $deserializer: expr) => { + enum $name_kind { + $( $variant ),* + } + + static VARIANTS: &'static [&'static str] = &[ $( stringify!($variant) ),*]; + + impl<'de> Deserialize<'de> for $name_kind { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct KindVisitor; + + impl<'de> Visitor<'de> for KindVisitor { + type Value = $name_kind; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("`V4` or `V6`") + } + + fn visit_u32(self, value: u32) -> Result + where + E: Error, + { + match value { + $( + $index => Ok($name_kind :: $variant), + )* + _ => Err(Error::invalid_value(Unexpected::Unsigned(value as u64), &self),), + } + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match value { + $( + stringify!($variant) => Ok($name_kind :: $variant), + )* + _ => Err(Error::unknown_variant(value, VARIANTS)), + } + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + match value { + $( + $bytes => Ok($name_kind :: $variant), + )* + _ => { + 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(KindVisitor) + } + } + + struct EnumVisitor; + impl<'de> Visitor<'de> for EnumVisitor { + type Value = $name; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(concat!("a ", stringify!($name))) + } + + + fn visit_enum(self, data: A) -> Result + where + A: EnumAccess<'de>, + { + match try!(data.variant()) { + $( + ($name_kind :: $variant, v) => v.newtype_variant().map($name :: $variant), + )* + } + } + } + $deserializer.deserialize_enum(stringify!($name), VARIANTS, EnumVisitor) + } +} + #[cfg(feature = "std")] impl<'de> Deserialize<'de> for net::IpAddr { fn deserialize(deserializer: D) -> Result @@ -897,28 +989,10 @@ impl<'de> Deserialize<'de> for net::IpAddr { let s = try!(String::deserialize(deserializer)); s.parse().map_err(Error::custom) } else { - struct EnumVisitor; - impl<'de> Visitor<'de> for EnumVisitor { - type Value = net::IpAddr; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a IpAddr") - } - - - fn visit_enum(self, data: A) -> Result - where - A: EnumAccess<'de>, - { - match try!(data.variant()) { - (0_u32, v) => v.newtype_variant().map(net::IpAddr::V4), - (1_u32, v) => v.newtype_variant().map(net::IpAddr::V6), - (_, _) => Err(Error::custom("Invalid IpAddr variant")), - } - } + use self::net::IpAddr; + deserialize_enum!{ + IpAddr IpAddrKind (V4; b"V4"; 0, V6; b"V6"; 1) deserializer } - const VARIANTS: &'static [&'static str] = &["V4", "V6"]; - deserializer.deserialize_enum("IpAddr", VARIANTS, EnumVisitor) } } } @@ -949,7 +1023,22 @@ macro_rules! parse_socket_impl { } #[cfg(feature = "std")] -parse_socket_impl!(net::SocketAddr, net::SocketAddr::new); +impl<'de> Deserialize<'de> for net::SocketAddr { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + if deserializer.is_human_readable() { + let s = try!(String::deserialize(deserializer)); + s.parse().map_err(Error::custom) + } else { + use self::net::SocketAddr; + deserialize_enum!{ + SocketAddr SocketAddrKind (V4; b"V4"; 0, V6; b"V6"; 1) deserializer + } + } + } +} #[cfg(feature = "std")] parse_socket_impl!(net::SocketAddrV4, net::SocketAddrV4::new); diff --git a/serde_test/src/assert.rs b/serde_test/src/assert.rs index 31eb622f..b95cb739 100644 --- a/serde_test/src/assert.rs +++ b/serde_test/src/assert.rs @@ -47,8 +47,18 @@ pub fn assert_tokens<'de, T>(value: &T, tokens: &'de [Token]) where T: Serialize + Deserialize<'de> + PartialEq + Debug, { - assert_ser_tokens(value, tokens); - assert_de_tokens(value, tokens); + assert_tokens_readable(value, tokens, true); +} + +/// Runs both `assert_ser_tokens` and `assert_de_tokens`. +/// +/// See: `assert_tokens` +pub fn assert_tokens_readable<'de, T>(value: &T, tokens: &'de [Token], human_readable: bool) +where + T: Serialize + Deserialize<'de> + PartialEq + Debug, +{ + assert_ser_tokens_readable(value, tokens, human_readable); + assert_de_tokens_readable(value, tokens, human_readable); } /// Asserts that `value` serializes to the given `tokens`. diff --git a/serde_test/src/lib.rs b/serde_test/src/lib.rs index 0ccf578a..b7c4ae92 100644 --- a/serde_test/src/lib.rs +++ b/serde_test/src/lib.rs @@ -168,7 +168,7 @@ mod token; mod assert; pub use token::Token; -pub use assert::{assert_tokens, assert_ser_tokens, assert_ser_tokens_error, assert_ser_tokens_readable, +pub use assert::{assert_tokens, assert_tokens_readable, assert_ser_tokens, assert_ser_tokens_error, assert_ser_tokens_readable, assert_de_tokens, assert_de_tokens_error, assert_de_tokens_readable}; // Not public API. diff --git a/test_suite/tests/test_de.rs b/test_suite/tests/test_de.rs index ec0100bd..5318fd88 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -781,9 +781,9 @@ declare_non_human_readable_tests!{ } test_non_human_readable_net_socketaddr { net::SocketAddr::from((*b"1234567890123456", 1234)) => &seq![ + Token::NewtypeVariant { name: "SocketAddr", variant: "V6" }, + Token::Tuple { len: 2 }, - Token::Enum { name: "IpAddr" }, - Token::U32(1), Token::Tuple { len: 16 }, seq b"1234567890123456".iter().map(|&b| Token::U8(b)), @@ -793,9 +793,9 @@ declare_non_human_readable_tests!{ Token::TupleEnd ], net::SocketAddr::from((*b"1234", 1234)) => &seq![ + Token::NewtypeVariant { name: "SocketAddr", variant: "V4" }, + Token::Tuple { len: 2 }, - Token::Enum { name: "IpAddr" }, - Token::U32(0), Token::Tuple { len: 4 }, seq b"1234".iter().map(|&b| Token::U8(b)), diff --git a/test_suite/tests/test_roundtrip.rs b/test_suite/tests/test_roundtrip.rs new file mode 100644 index 00000000..eee7e83c --- /dev/null +++ b/test_suite/tests/test_roundtrip.rs @@ -0,0 +1,45 @@ +extern crate serde_test; +use self::serde_test::{Token, assert_tokens_readable}; + +use std::net; + +#[macro_use] +#[allow(unused_macros)] +mod macros; + +#[test] +fn ip_addr_roundtrip() { + + assert_tokens_readable( + &net::IpAddr::from(*b"1234"), + &seq![ + Token::NewtypeVariant { name: "IpAddr", variant: "V4" }, + + Token::Tuple { len: 4 }, + seq b"1234".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + ], + false, + ); +} + +#[test] +fn socked_addr_roundtrip() { + + assert_tokens_readable( + &net::SocketAddr::from((*b"1234567890123456", 1234)), + &seq![ + Token::NewtypeVariant { name: "SocketAddr", variant: "V6" }, + + Token::Tuple { len: 2 }, + + Token::Tuple { len: 16 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd, + ], + false, + ); +} From 945d12c0b4bb218e2c17a43cdefd2021382af1b5 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Thu, 14 Sep 2017 17:04:26 +0200 Subject: [PATCH 08/10] Use the variant_identifier macro for OsString --- serde/src/de/impls.rs | 106 ++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 72 deletions(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index 9b68ebf7..d4f0b391 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -887,13 +887,17 @@ macro_rules! parse_ip_impl { } } -macro_rules! deserialize_enum { - ($name: ident $name_kind: ident ( $($variant: ident; $bytes: expr; $index: expr),* ) $deserializer: expr) => { +macro_rules! variant_identifier { + ( + $name_kind: ident ( $($variant: ident; $bytes: expr; $index: expr),* ) + $expecting_message: expr, + $variants_name: ident + ) => { enum $name_kind { $( $variant ),* } - static VARIANTS: &'static [&'static str] = &[ $( stringify!($variant) ),*]; + static $variants_name: &'static [&'static str] = &[ $( stringify!($variant) ),*]; impl<'de> Deserialize<'de> for $name_kind { fn deserialize(deserializer: D) -> Result @@ -906,7 +910,7 @@ macro_rules! deserialize_enum { type Value = $name_kind; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("`V4` or `V6`") + formatter.write_str($expecting_message) } fn visit_u32(self, value: u32) -> Result @@ -929,7 +933,7 @@ macro_rules! deserialize_enum { $( stringify!($variant) => Ok($name_kind :: $variant), )* - _ => Err(Error::unknown_variant(value, VARIANTS)), + _ => Err(Error::unknown_variant(value, $variants_name)), } } @@ -943,7 +947,7 @@ macro_rules! deserialize_enum { )* _ => { match str::from_utf8(value) { - Ok(value) => Err(Error::unknown_variant(value, VARIANTS)), + Ok(value) => Err(Error::unknown_variant(value, $variants_name)), Err(_) => Err(Error::invalid_value(Unexpected::Bytes(value), &self)), } } @@ -954,6 +958,20 @@ macro_rules! deserialize_enum { deserializer.deserialize_identifier(KindVisitor) } } + } +} + +macro_rules! deserialize_enum { + ( + $name: ident $name_kind: ident ( $($variant: ident; $bytes: expr; $index: expr),* ) + $expecting_message: expr, + $deserializer: expr + ) => { + variant_identifier!{ + $name_kind ( $($variant; $bytes; $index),* ) + $expecting_message, + VARIANTS + } struct EnumVisitor; impl<'de> Visitor<'de> for EnumVisitor { @@ -991,7 +1009,9 @@ impl<'de> Deserialize<'de> for net::IpAddr { } else { use self::net::IpAddr; deserialize_enum!{ - IpAddr IpAddrKind (V4; b"V4"; 0, V6; b"V6"; 1) deserializer + IpAddr IpAddrKind (V4; b"V4"; 0, V6; b"V6"; 1) + "`V4` or `V6`", + deserializer } } } @@ -1034,7 +1054,9 @@ impl<'de> Deserialize<'de> for net::SocketAddr { } else { use self::net::SocketAddr; deserialize_enum!{ - SocketAddr SocketAddrKind (V4; b"V4"; 0, V6; b"V6"; 1) deserializer + SocketAddr SocketAddrKind (V4; b"V4"; 0, V6; b"V6"; 1) + "`V4` or `V6`", + deserializer } } } @@ -1129,70 +1151,10 @@ impl<'de> Deserialize<'de> for PathBuf { // #[derive(Deserialize)] // #[serde(variant_identifier)] #[cfg(all(feature = "std", any(unix, windows)))] -enum OsStringKind { - Unix, - Windows, -} - -#[cfg(all(feature = "std", any(unix, windows)))] -static OSSTR_VARIANTS: &'static [&'static str] = &["Unix", "Windows"]; - -#[cfg(all(feature = "std", any(unix, windows)))] -impl<'de> Deserialize<'de> for OsStringKind { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct KindVisitor; - - impl<'de> Visitor<'de> for KindVisitor { - type Value = OsStringKind; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("`Unix` or `Windows`") - } - - fn visit_u32(self, value: u32) -> Result - where - E: Error, - { - match value { - 0 => Ok(OsStringKind::Unix), - 1 => Ok(OsStringKind::Windows), - _ => Err(Error::invalid_value(Unexpected::Unsigned(value as u64), &self),), - } - } - - fn visit_str(self, value: &str) -> Result - where - E: Error, - { - match value { - "Unix" => Ok(OsStringKind::Unix), - "Windows" => Ok(OsStringKind::Windows), - _ => Err(Error::unknown_variant(value, OSSTR_VARIANTS)), - } - } - - fn visit_bytes(self, value: &[u8]) -> Result - where - E: Error, - { - match value { - b"Unix" => Ok(OsStringKind::Unix), - b"Windows" => Ok(OsStringKind::Windows), - _ => { - match str::from_utf8(value) { - Ok(value) => Err(Error::unknown_variant(value, OSSTR_VARIANTS)), - Err(_) => Err(Error::invalid_value(Unexpected::Bytes(value), &self)), - } - } - } - } - } - - deserializer.deserialize_identifier(KindVisitor) - } +variant_identifier!{ + OsStringKind (Unix; b"Unix"; 0, Windows; b"Windows"; 1) + "`Unix` or `Windows`", + OSSTR_VARIANTS } #[cfg(all(feature = "std", any(unix, windows)))] From 3b135431fd09dca5c87dd10e89ce6f33cd13188e Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Thu, 14 Sep 2017 17:12:23 +0200 Subject: [PATCH 09/10] Try to fix compilation on 1.13 --- serde/src/de/impls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index d4f0b391..c48d02a7 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -1007,7 +1007,7 @@ impl<'de> Deserialize<'de> for net::IpAddr { let s = try!(String::deserialize(deserializer)); s.parse().map_err(Error::custom) } else { - use self::net::IpAddr; + use lib::net::IpAddr; deserialize_enum!{ IpAddr IpAddrKind (V4; b"V4"; 0, V6; b"V6"; 1) "`V4` or `V6`", @@ -1052,7 +1052,7 @@ impl<'de> Deserialize<'de> for net::SocketAddr { let s = try!(String::deserialize(deserializer)); s.parse().map_err(Error::custom) } else { - use self::net::SocketAddr; + use lib::net::SocketAddr; deserialize_enum!{ SocketAddr SocketAddrKind (V4; b"V4"; 0, V6; b"V6"; 1) "`V4` or `V6`", From e9b530a000442052c5d7e1dbaa74cbc833376d30 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 13 Oct 2017 17:33:03 +0200 Subject: [PATCH 10/10] Hide is_human_readable constructors in serde_test Until a good API can be found --- serde_test/src/assert.rs | 6 ++++++ serde_test/src/de.rs | 2 ++ serde_test/src/lib.rs | 8 ++++++-- serde_test/src/ser.rs | 2 ++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/serde_test/src/assert.rs b/serde_test/src/assert.rs index b95cb739..7a4b1d4d 100644 --- a/serde_test/src/assert.rs +++ b/serde_test/src/assert.rs @@ -50,6 +50,8 @@ where assert_tokens_readable(value, tokens, true); } +// Not public API +#[doc(hidden)] /// Runs both `assert_ser_tokens` and `assert_de_tokens`. /// /// See: `assert_tokens` @@ -97,6 +99,8 @@ where assert_ser_tokens_readable(value, tokens, true) } +// Not public API +#[doc(hidden)] /// Asserts that `value` serializes to the given `tokens`. /// /// See: `assert_ser_tokens` @@ -206,6 +210,8 @@ where assert_de_tokens_readable(value, tokens, true) } +// Not public API +#[doc(hidden)] pub fn assert_de_tokens_readable<'de, T>(value: &T, tokens: &'de [Token], human_readable: bool) where T: Deserialize<'de> + PartialEq + Debug, diff --git a/serde_test/src/de.rs b/serde_test/src/de.rs index 4b0bc8ab..8a281d9d 100644 --- a/serde_test/src/de.rs +++ b/serde_test/src/de.rs @@ -52,6 +52,8 @@ impl<'de> Deserializer<'de> { Deserializer::readable(tokens, true) } + // Not public API + #[doc(hidden)] pub fn readable(tokens: &'de [Token], is_human_readable: bool) -> Self { Deserializer { tokens: tokens, is_human_readable: is_human_readable } } diff --git a/serde_test/src/lib.rs b/serde_test/src/lib.rs index b7c4ae92..7833d942 100644 --- a/serde_test/src/lib.rs +++ b/serde_test/src/lib.rs @@ -168,8 +168,12 @@ mod token; mod assert; pub use token::Token; -pub use assert::{assert_tokens, assert_tokens_readable, assert_ser_tokens, assert_ser_tokens_error, assert_ser_tokens_readable, - assert_de_tokens, assert_de_tokens_error, assert_de_tokens_readable}; +pub use assert::{assert_tokens, assert_ser_tokens, assert_ser_tokens_error, + assert_de_tokens, assert_de_tokens_error}; + +// Not public API. +#[doc(hidden)] +pub use assert::{assert_tokens_readable, assert_de_tokens_readable, assert_ser_tokens_readable}; // Not public API. #[doc(hidden)] diff --git a/serde_test/src/ser.rs b/serde_test/src/ser.rs index c7d64d29..a25ade9a 100644 --- a/serde_test/src/ser.rs +++ b/serde_test/src/ser.rs @@ -24,6 +24,8 @@ impl<'a> Serializer<'a> { Serializer::readable(tokens, true) } + // Not public API + #[doc(hidden)] pub fn readable(tokens: &'a [Token], is_human_readable: bool) -> Self { Serializer { tokens: tokens, is_human_readable: is_human_readable } }