address 0.19.0

This library aids in processing network addresses.
#![cfg(feature = "serde")]

use address::{
    Authority, AuthorityRef, Domain, DomainRef, Endpoint, EndpointRef, Host, HostRef, IPAddress,
    IPv4Address, IPv6Address, SocketAddress, SocketAddressV4, SocketAddressV6,
};
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt::Debug;
use std::str::FromStr;

const IPV4_ADDRESSES: &[&str] = &["0.0.0.0", "127.0.0.1", "1.2.3.4", "255.255.255.255"];
const IPV6_ADDRESSES: &[&str] = &[
    "::",
    "::1",
    "1::",
    "1::1",
    "1:0:0:1::",
    "1:2:3:4:5:6:7:8",
    "fe80::1",
    "::ffff:1.2.3.4",
    "2001:db8::8a2e:370:7334",
    "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
];
const IP_ADDRESSES: &[&str] = &["127.0.0.1", "255.255.255.255", "::1", "fe80::1"];
const SOCKET_ADDRESSES_V4: &[&str] = &["0.0.0.0:0", "127.0.0.1:80", "255.255.255.255:65535"];
const SOCKET_ADDRESSES_V6: &[&str] = &[
    "[::]:0",
    "[::1]:80",
    "[::ffff:1.2.3.4]:443",
    "[fe80::1]:65535",
];
const SOCKET_ADDRESSES: &[&str] = &["127.0.0.1:80", "[::1]:443", "[fe80::1]:0"];
const DOMAINS: &[&str] = &[
    "localhost",
    "example.com",
    "a-b.c--d.example",
    "xn--bcher-kva.example",
    "123.example",
];
const ENDPOINTS: &[&str] = &["localhost:80", "example.com:443", "a.b.c:65535", "x:0"];
const HOSTS: &[&str] = &["localhost", "example.com", "127.0.0.1", "::1", "fe80::1"];
const AUTHORITIES: &[&str] = &[
    "localhost:80",
    "example.com:443",
    "127.0.0.1:80",
    "[::1]:443",
    "[fe80::1]:0",
];

fn assert_round_trips<T>(canonical: &[&str])
where
    T: FromStr + Serialize + DeserializeOwned + PartialEq + Debug,
    T::Err: Debug,
{
    for s in canonical {
        let value: T = T::from_str(s).unwrap();

        let json: String = serde_json::to_string(&value).unwrap();
        assert_eq!(json, format!("\"{}\"", s));
        let parsed: T = serde_json::from_str(json.as_str()).unwrap();
        assert_eq!(parsed, value);

        let bytes: Vec<u8> = postcard::to_allocvec(&value).unwrap();
        let parsed: T = postcard::from_bytes(bytes.as_slice()).unwrap();
        assert_eq!(parsed, value);
    }
}

macro_rules! assert_ref_round_trips {
    ($ty:ident, $canonical:expr) => {
        for s in $canonical {
            let value: $ty = $ty::try_from(*s).unwrap();

            let json: String = serde_json::to_string(&value).unwrap();
            assert_eq!(json, format!("\"{}\"", s));
            let parsed: $ty = serde_json::from_str(json.as_str()).unwrap();
            assert_eq!(parsed, value);

            let bytes: Vec<u8> = postcard::to_allocvec(&value).unwrap();
            let parsed: $ty = postcard::from_bytes(bytes.as_slice()).unwrap();
            assert_eq!(parsed, value);
        }
    };
}

#[test]
fn ipv4_address() {
    assert_round_trips::<IPv4Address>(IPV4_ADDRESSES);
}

#[test]
fn ipv6_address() {
    assert_round_trips::<IPv6Address>(IPV6_ADDRESSES);
}

#[test]
fn ip_address() {
    assert_round_trips::<IPAddress>(IP_ADDRESSES);
}

#[test]
fn socket_address_v4() {
    assert_round_trips::<SocketAddressV4>(SOCKET_ADDRESSES_V4);
}

#[test]
fn socket_address_v6() {
    assert_round_trips::<SocketAddressV6>(SOCKET_ADDRESSES_V6);
}

#[test]
fn socket_address() {
    assert_round_trips::<SocketAddress>(SOCKET_ADDRESSES);
}

#[test]
fn domain() {
    assert_round_trips::<Domain>(DOMAINS);
}

#[test]
fn domain_ref() {
    assert_ref_round_trips!(DomainRef, DOMAINS);
}

#[test]
fn endpoint() {
    assert_round_trips::<Endpoint>(ENDPOINTS);
}

#[test]
fn endpoint_ref() {
    assert_ref_round_trips!(EndpointRef, ENDPOINTS);
}

#[test]
fn host() {
    assert_round_trips::<Host>(HOSTS);
}

#[test]
fn host_ref() {
    assert_ref_round_trips!(HostRef, HOSTS);
}

#[test]
fn authority() {
    assert_round_trips::<Authority>(AUTHORITIES);
}

#[test]
fn authority_ref() {
    assert_ref_round_trips!(AuthorityRef, AUTHORITIES);
}

#[test]
fn escaped_input() {
    let domain: Domain = serde_json::from_str("\"local\\u0068ost\"").unwrap();
    assert_eq!(domain, Domain::localhost());

    let result: Result<DomainRef, serde_json::Error> = serde_json::from_str("\"local\\u0068ost\"");
    assert!(result.is_err());
}

#[test]
fn compact_sizes() {
    let bytes: Vec<u8> = postcard::to_allocvec(&IPv4Address::LOCALHOST).unwrap();
    assert_eq!(bytes.len(), 4);

    let bytes: Vec<u8> = postcard::to_allocvec(&IPv6Address::LOCALHOST).unwrap();
    assert_eq!(bytes.len(), 16);

    let bytes: Vec<u8> = postcard::to_allocvec(&IPv4Address::LOCALHOST.to_ip()).unwrap();
    assert_eq!(bytes.len(), 5);

    let bytes: Vec<u8> = postcard::to_allocvec(&IPv6Address::LOCALHOST.to_ip()).unwrap();
    assert_eq!(bytes.len(), 17);
}