diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index ef3e20e6..c48d02a7 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -869,37 +869,204 @@ map_impl!( //////////////////////////////////////////////////////////////////////////////// #[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) + if deserializer.is_human_readable() { + let s = try!(String::deserialize(deserializer)); + s.parse().map_err(Error::custom) + } else { + <[u8; $size]>::deserialize(deserializer).map(<$ty>::from) + } + } + } + } +} + +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_name: &'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($expecting_message) + } + + 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_name)), + } + } + + 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_name)), + Err(_) => Err(Error::invalid_value(Unexpected::Bytes(value), &self)), + } + } + } + } + } + + 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 { + 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 + where + D: Deserializer<'de>, + { + if deserializer.is_human_readable() { + let s = try!(String::deserialize(deserializer)); + s.parse().map_err(Error::custom) + } else { + use lib::net::IpAddr; + deserialize_enum!{ + IpAddr IpAddrKind (V4; b"V4"; 0, V6; b"V6"; 1) + "`V4` or `V6`", + deserializer } } } } #[cfg(feature = "std")] -parse_impl!(net::IpAddr); +parse_ip_impl!(net::Ipv4Addr; 4); #[cfg(feature = "std")] -parse_impl!(net::Ipv4Addr); +parse_ip_impl!(net::Ipv6Addr; 16); #[cfg(feature = "std")] -parse_impl!(net::Ipv6Addr); +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::SocketAddr); +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 lib::net::SocketAddr; + deserialize_enum!{ + SocketAddr SocketAddrKind (V4; b"V4"; 0, V6; b"V6"; 1) + "`V4` or `V6`", + deserializer + } + } + } +} #[cfg(feature = "std")] -parse_impl!(net::SocketAddrV4); +parse_socket_impl!(net::SocketAddrV4, net::SocketAddrV4::new); #[cfg(feature = "std")] -parse_impl!(net::SocketAddrV6); +parse_socket_impl!(net::SocketAddrV6, |ip, port| net::SocketAddrV6::new(ip, port, 0, 0)); //////////////////////////////////////////////////////////////////////////////// @@ -984,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)))] diff --git a/serde/src/de/mod.rs b/serde/src/de/mod.rs index eb100951..a574a1ce 100644 --- a/serde/src/de/mod.rs +++ b/serde/src/de/mod.rs @@ -1011,6 +1011,15 @@ 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. + /// + /// 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 ea59b78d..3fa2ad9c 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -506,9 +506,18 @@ 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) => + serializer.serialize_newtype_variant("IpAddr", 0, "V4", a), + net::IpAddr::V6(ref a) => + serializer.serialize_newtype_variant("IpAddr", 1, "V6", a), + } } } } @@ -519,9 +528,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 +544,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) + } } } @@ -543,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), + } } } } @@ -556,9 +582,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(), self.port()).serialize(serializer) + } } } @@ -568,9 +598,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(), self.port()).serialize(serializer) + } } } diff --git a/serde/src/ser/mod.rs b/serde/src/ser/mod.rs index e934adb9..8ba8fa2b 100644 --- a/serde/src/ser/mod.rs +++ b/serde/src/ser/mod.rs @@ -1363,6 +1363,15 @@ 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. + /// + /// 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 } } /// Returned from `Serializer::serialize_seq`. diff --git a/serde_test/src/assert.rs b/serde_test/src/assert.rs index 2af2ebf5..7a4b1d4d 100644 --- a/serde_test/src/assert.rs +++ b/serde_test/src/assert.rs @@ -47,8 +47,20 @@ 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); +} + +// Not public API +#[doc(hidden)] +/// 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`. @@ -84,7 +96,19 @@ 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) +} + +// Not public API +#[doc(hidden)] +/// 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 +207,16 @@ 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) +} + +// 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, +{ + 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..8a281d9d 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,13 @@ macro_rules! end_of_tokens { impl<'de> Deserializer<'de> { pub fn new(tokens: &'de [Token]) -> Self { - Deserializer { tokens: tokens } + 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 } } fn peek_token_opt(&self) -> Option { @@ -364,6 +371,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 2aba94a6..53ee38d2 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_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_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 31ba72ee..a25ade9a 100644 --- a/serde_test/src/ser.rs +++ b/serde_test/src/ser.rs @@ -15,12 +15,19 @@ 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) + } + + // 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 } } /// Pulls the next token off of the serializer, ignoring it. @@ -282,6 +289,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/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 da7232a5..082e4519 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; @@ -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 }); } @@ -763,6 +772,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::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 + ], + net::SocketAddr::from((*b"1234", 1234)) => &seq![ + Token::NewtypeVariant { name: "SocketAddr", variant: "V4" }, + + 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::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 { @@ -804,7 +877,7 @@ fn test_osstring() { ]; assert_de_tokens(&value, &tokens); - assert_de_tokens_ignore(&tokens); + assert_de_tokens_ignore(&tokens, true); } #[cfg(windows)] @@ -824,7 +897,7 @@ fn test_osstring() { ]; assert_de_tokens(&value, &tokens); - assert_de_tokens_ignore(&tokens); + assert_de_tokens_ignore(&tokens, true); } #[cfg(feature = "unstable")] @@ -1087,3 +1160,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_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, + ); +} diff --git a/test_suite/tests/test_ser.rs b/test_suite/tests/test_ser.rs index 9dfb7053..3f3fa61c 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; @@ -77,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], @@ -397,6 +411,66 @@ 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_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 }, + 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! { @@ -474,3 +548,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); +}