Support for ipv6 scope id

Hi,

std::ne::Ipv6Addr is currently unable to parse IPv6 addresses with a scope identifier, like FE80::C001:1DFF:FEE0:0%eth0, as defined in RFC 4007. It is possible to work around that limitation by using something like:

use std::net::{Ipv4Addr, Ipv6Addr};
pub enum Ip {
    V4(Ipv4Addr),
    V6(Ipv6Addr, Option<String>),
}

And then implement FromStr for Ip.
But it's pretty inconvenient, and I'd rather use std::net::IpAddr. Would it be possible to support this directly in std::net::Ipv6Addr? If so, what is the right path to make this happen? Open an issue on github?

Ipv6Addr is Copy so it won't be possible to support scope IDs in that type itself.

All right... I was feeling that the String would make that complicated.

Even if you could somehow maintain the Copy aspect, it’s likely you wouldn’t want to waste any space on a (AFAICT) rarely specified part of the address. To that end, a custom type (whether in std or otherwise) seems more appropriate anyway.

That makes sense.
I created a custom type like this, for now:

use std::net::{Ipv4Addr, Ipv6Addr};
/// Represent an IP address. This type is similar to `std::net::IpAddr` but it supports IPv6 scope
/// identifiers.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Ip {
    /// Represent an IPv4 address
    V4(Ipv4Addr),
    /// Represent an IPv6 and its scope identifier, if any
    V6(Ipv6Addr, Option<String>),
}

impl Into<IpAddr> for Ip {
    fn into(self) -> IpAddr {
        match self {
            Ip::V4(ip) => IpAddr::from(ip),
            Ip::V6(ip, _) => IpAddr::from(ip),
        }
    }
}

impl From<Ipv6Addr> for Ip {
    fn from(value: Ipv6Addr) -> Self {
        Ip::V6(value, None)
    }
}

impl From<Ipv4Addr> for Ip {
    fn from(value: Ipv4Addr) -> Self {
        Ip::V4(value)
    }
}

impl From<IpAddr> for Ip {
    fn from(value: IpAddr) -> Self {
        match value {
            IpAddr::V4(ip) => Ip::from(ip),
            IpAddr::V6(ip) => Ip::from(ip),
        }
    }
}

impl Ip {
    /// Parse a string representing an IP address.
    pub fn parse(s: &str) -> Result<Ip, AddrParseError> {
        let mut parts = s.split('%');
        let addr = parts.next().unwrap();
        match IpAddr::from_str(addr) {
            Ok(IpAddr::V4(ip)) => Ok(Ip::from(ip)),
            Ok(IpAddr::V6(ip)) => Ok(Ip::V6(ip, parts.next().map(|s| s.to_string()))),
            Err(e) => Err(e),
        }
    }