use crate::{IPAddress, IPv4Address, IPv6Address};
macro_rules! impl_serde_string_or_binary {
($ty:ident, $expecting:literal, $bin:ty $(, $doc:expr)*) => {
impl ::serde::Serialize for crate::$ty {
#[doc = concat!(
"Serializes as the `Display` string in human-readable formats and as `",
stringify!($bin),
"` in others."
)]
$(
#[doc = ""]
#[doc = $doc]
)*
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::Serializer,
{
if serializer.is_human_readable() {
serializer.collect_str(self)
} else {
let binary: $bin = <$bin>::from(*self);
::serde::Serialize::serialize(&binary, serializer)
}
}
}
impl<'de> ::serde::Deserialize<'de> for crate::$ty {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
if deserializer.is_human_readable() {
deserializer.deserialize_str(crate::serde::FromStrVisitor::new($expecting))
} else {
<$bin as ::serde::Deserialize>::deserialize(deserializer).map(crate::$ty::from)
}
}
}
};
}
impl_serde_string_or_binary!(
IPv4Address,
"an IPv4 address string",
[u8; 4],
"The binary form is byte-identical to the standard library's."
);
impl_serde_string_or_binary!(
IPv6Address,
"an IPv6 address string",
[u8; 16],
"The binary form is byte-identical to the standard library's."
);
impl_serde_string_or_binary!(
SocketAddress,
"a socket address string",
(IPAddress, u16),
"The IP address is a byte string, not the standard library's enum tag."
);
impl_serde_string_or_binary!(
SocketAddressV4,
"an IPv4 socket address string",
(IPv4Address, u16),
"The binary form is byte-identical to the standard library's."
);
impl_serde_string_or_binary!(
SocketAddressV6,
"an IPv6 socket address string",
(IPv6Address, u16),
"The binary form is byte-identical to the standard library's."
);
#[cfg(test)]
mod tests {
use crate::serde::test_util::{assert_json, assert_postcard};
use crate::{IPv4Address, IPv6Address, SocketAddress, SocketAddressV4, SocketAddressV6};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
#[test]
fn json() {
assert_json(IPv4Address::UNSPECIFIED, "\"0.0.0.0\"");
assert_json(IPv4Address::LOCALHOST, "\"127.0.0.1\"");
assert_json(IPv6Address::LOCALHOST, "\"::1\"");
assert_json(IPv4Address::LOCALHOST.to_socket(80), "\"127.0.0.1:80\"");
assert_json(IPv6Address::LOCALHOST.to_socket(80), "\"[::1]:80\"");
assert_json(
IPv4Address::LOCALHOST.to_ip().to_socket(80),
"\"127.0.0.1:80\"",
);
assert_json(
IPv6Address::LOCALHOST.to_ip().to_socket(443),
"\"[::1]:443\"",
);
}
#[test]
fn postcard() {
assert_postcard(IPv4Address::BROADCAST);
assert_postcard(IPv6Address::UNSPECIFIED);
assert_postcard(IPv4Address::LOCALHOST.to_socket(65535));
assert_postcard(IPv6Address::LOCALHOST.to_socket(0));
assert_postcard(IPv6Address::LOCALHOST.to_ip().to_socket(443));
}
#[test]
fn compact_sizes() {
let test_cases: &[(Vec<u8>, usize, &str)] = &[
(assert_postcard(IPv4Address::LOCALHOST), 4, "IPv4Address"),
(assert_postcard(IPv6Address::LOCALHOST), 16, "IPv6Address"),
(
assert_postcard(IPv4Address::LOCALHOST.to_socket(80)),
5,
"SocketAddressV4",
),
(
assert_postcard(IPv6Address::LOCALHOST.to_socket(80)),
17,
"SocketAddressV6",
),
];
for (bytes, expected, label) in test_cases {
assert_eq!(bytes.len(), *expected, "type={}", label);
}
}
#[test]
fn matches_std_wire_format() {
let ip: IPv4Address = IPv4Address::LOCALHOST;
assert_eq!(
assert_postcard(ip),
postcard::to_allocvec(&Ipv4Addr::LOCALHOST).unwrap()
);
let ip: IPv6Address = IPv6Address::LOCALHOST;
assert_eq!(
assert_postcard(ip),
postcard::to_allocvec(&Ipv6Addr::LOCALHOST).unwrap()
);
let socket: SocketAddressV4 = IPv4Address::LOCALHOST.to_socket(80);
let std: SocketAddrV4 = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 80);
assert_eq!(
assert_postcard(socket),
postcard::to_allocvec(&std).unwrap()
);
let socket: SocketAddressV6 = IPv6Address::LOCALHOST.to_socket(80);
let std: SocketAddrV6 = SocketAddrV6::new(Ipv6Addr::LOCALHOST, 80, 0, 0);
assert_eq!(
assert_postcard(socket),
postcard::to_allocvec(&std).unwrap()
);
}
#[test]
fn socket_address_diverges_from_std() {
let socket: SocketAddress = IPv4Address::LOCALHOST.to_ip().to_socket(80);
let bytes: Vec<u8> = assert_postcard(socket);
assert_eq!(
bytes.len(),
6,
"4 address bytes with a length prefix, plus the port"
);
}
}