Pre-ACP(?): Parse `SocketAddr` with default port

It's fairly common to want to accept a command-line argument, or similar, that's either a full socket address (IPv4 w.x.y.z:port, IPv6 [ww:xx:yy:zz::aa(optional stuff)]:port) or a bare IP address. In the latter case, the program will fill in the usual port for whatever protocol it speaks. For example here's some code I just wrote

const DNS_PORT: u16 = 53;
impl FromStr for ResolverArg {
    type Err = ResolverArgParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s == "system" {
            // use system default resolver
            return Ok(Self::System);
        }
        if let Ok(addr) = s.parse::<IpAddr>() {
            return Ok(Self::Ip(SocketAddr::new(addr, DNS_PORT)));
        }
        if let Ok(addr) = s.parse::<SocketAddr>() {
            return Ok(Self::Ip(addr));
        }
        Err(ResolverArgParseError)
    }
}

But, this is tedious to write, it does redundant work when the IpAddr parse fails due to a trailing port number, and the user can't specify the IPv6 scope ID without also specifying the port. I'd like to be able to write this instead:

        // ...
        if let Ok(addr) = SocketAddr::from_str_with_default_port(DNS_PORT) {
            return Ok(Self::Ip(addr));
        }
        Err(ResolverArgParseError)

or maybe this

        // ...
        if let Ok(addr) = s.parse::<SocketAddr>()
            .or_else(|e| e.try_with_default_port(DNS_PORT))
        {
            return Ok(Self::Ip(addr));
        }
        Err(ResolverArgParseError)

What do people think? Is this worth adding?

(N.B. hostnames are out of scope because parsing a hostname is thorny and converting a hostname to an IP address involves doing I/O.)

1 Like