address 0.23.0

Network address types with strict validation, owned & borrowed variants, and standard library conversions.
Documentation
use crate::{Host, HostRef, IPAddress, IPv4Address, IPv6Address, SocketAddressV6};

impl IPv6Address {
    //! Conversions

    /// Converts an IPv4-compatible or IPv4-mapped address to an IPv4 address.
    ///
    /// Any address in `::/96` matches the compatible pattern, so `::1` converts to `0.0.0.1` even
    /// though it is the IPv6 loopback, not an embedded IPv4 address. Use [`Self::to_v4_mapped`]
    /// to accept only mapped addresses.
    pub const fn to_v4(self) -> Result<IPv4Address, Self> {
        match self.address() {
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d] => Ok(IPv4Address::new([a, b, c, d])),
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d] => {
                Ok(IPv4Address::new([a, b, c, d]))
            }
            _ => Err(self),
        }
    }

    /// Converts an IPv4-mapped (::ffff:a.b.c.d) address to an IPv4 address.
    ///
    /// Unlike [`Self::to_v4`], IPv4-compatible addresses (::a.b.c.d) are an error.
    pub const fn to_v4_mapped(self) -> Result<IPv4Address, Self> {
        match self.address() {
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d] => {
                Ok(IPv4Address::new([a, b, c, d]))
            }
            _ => Err(self),
        }
    }

    /// Converts the address to an IP address.
    pub const fn to_ip(self) -> IPAddress {
        IPAddress::V6(self)
    }

    /// Converts the address to a socket address with the `port`.
    pub const fn to_socket(self, port: u16) -> SocketAddressV6 {
        SocketAddressV6::new(self, port)
    }

    /// Converts the address to a host.
    pub const fn to_host(self) -> Host {
        Host::IPAddress(self.to_ip())
    }

    /// Converts the address to a host reference.
    pub const fn to_host_ref(self) -> HostRef<'static> {
        HostRef::IPAddress(self.to_ip())
    }
}

impl TryFrom<IPAddress> for IPv6Address {
    type Error = IPAddress;

    fn try_from(ip: IPAddress) -> Result<Self, Self::Error> {
        ip.to_v6()
    }
}

#[cfg(test)]
mod tests {
    use crate::{Host, HostRef, IPAddress, IPv4Address, IPv6Address, SocketAddressV6};

    #[test]
    fn v6_to_v4() {
        let test_cases: &[(IPv6Address, Result<IPv4Address, IPv6Address>)] = &[
            (
                IPv6Address::from([0, 0, 0, 0, 0, 0, 0x7F00, 1]),
                Ok(IPv4Address::LOCALHOST),
            ),
            (
                IPv6Address::from([0, 0, 0, 0, 0, 0xFFFF, 0x7F00, 1]),
                Ok(IPv4Address::LOCALHOST),
            ),
            (
                IPv6Address::from([1, 0, 0, 0, 0, 0, 0, 0]),
                Err(IPv6Address::from([1, 0, 0, 0, 0, 0, 0, 0])),
            ),
            (
                IPv6Address::from([0, 0, 0, 0, 0, 1, 0, 0]),
                Err(IPv6Address::from([0, 0, 0, 0, 0, 1, 0, 0])),
            ),
        ];

        for (ip, expected) in test_cases {
            let result: Result<IPv4Address, IPv6Address> = ip.to_v4();
            assert_eq!(result, *expected, "ip={}", ip);
        }

        let ip: IPv6Address = IPv6Address::from([0, 0, 0, 0, 0, 0xFFFF, 0x7F00, 1]);
        let result: Result<IPv4Address, IPv6Address> = ip.to_v4_mapped();
        let expected: Result<IPv4Address, IPv6Address> = Ok(IPv4Address::LOCALHOST);
        assert_eq!(result, expected);

        let ip: IPv6Address = IPv6Address::from([0, 0, 0, 0, 0, 0, 0x7F00, 1]);
        let result: Result<IPv4Address, IPv6Address> = ip.to_v4_mapped();
        let expected: Result<IPv4Address, IPv6Address> = Err(ip);
        assert_eq!(result, expected);
    }

    #[test]
    fn v6_to_ip() {
        let ip: IPv6Address = IPv6Address::LOCALHOST;
        let result: IPAddress = ip.to_ip();
        let expected: IPAddress = IPAddress::V6(IPv6Address::LOCALHOST);
        assert_eq!(result, expected);
    }

    #[test]
    fn v6_to_socket() {
        let ip: IPv6Address = IPv6Address::LOCALHOST;
        let result: SocketAddressV6 = ip.to_socket(80);
        let expected: SocketAddressV6 = SocketAddressV6::new(IPv6Address::LOCALHOST, 80);
        assert_eq!(result, expected);
    }

    #[test]
    fn v6_to_host() {
        let ip: IPv6Address = IPv6Address::LOCALHOST;

        let result: Host = ip.to_host();
        let expected: Host = Host::IPAddress(IPAddress::V6(IPv6Address::LOCALHOST));
        assert_eq!(result, expected);

        let result: HostRef = ip.to_host_ref();
        let expected: HostRef = HostRef::IPAddress(IPAddress::V6(IPv6Address::LOCALHOST));
        assert_eq!(result, expected);
    }

    #[test]
    fn v6_try_from() {
        let ip: IPAddress = IPv6Address::LOCALHOST.to_ip();
        assert_eq!(IPv6Address::try_from(ip), Ok(IPv6Address::LOCALHOST));

        let ip: IPAddress = IPv4Address::LOCALHOST.to_ip();
        assert_eq!(IPv6Address::try_from(ip), Err(ip));
    }
}