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.)