Properly deserialize non-readable IpAddr and SocketAddr
This commit is contained in:
parent
85c05d301a
commit
e36915300f
@ -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<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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<E>(self, value: u32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
match value {
|
||||
$(
|
||||
$index => Ok($name_kind :: $variant),
|
||||
)*
|
||||
_ => Err(Error::invalid_value(Unexpected::Unsigned(value as u64), &self),),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
match value {
|
||||
$(
|
||||
stringify!($variant) => Ok($name_kind :: $variant),
|
||||
)*
|
||||
_ => Err(Error::unknown_variant(value, VARIANTS)),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
||||
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<A>(self, data: A) -> Result<Self::Value, A::Error>
|
||||
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<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
@ -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<A>(self, data: A) -> Result<Self::Value, A::Error>
|
||||
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<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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);
|
||||
|
@ -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`.
|
||||
|
@ -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.
|
||||
|
@ -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)),
|
||||
|
45
test_suite/tests/test_roundtrip.rs
Normal file
45
test_suite/tests/test_roundtrip.rs
Normal file
@ -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,
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user