use std::fmt;
use std::net;
#[cfg(unix)]
use std::os::unix::net as unix;
#[cfg(unix)]
use std::path::{Path, PathBuf};
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Addr {
Inet(net::SocketAddr),
#[cfg(unix)]
#[cfg_attr(docsrs, doc(cfg(unix)))]
Unix(PathBuf),
}
impl From<net::SocketAddr> for Addr {
fn from(s: net::SocketAddr) -> Addr {
Addr::Inet(s)
}
}
#[cfg(unix)]
impl From<&Path> for Addr {
fn from(s: &Path) -> Addr {
Addr::Unix(s.to_path_buf())
}
}
#[cfg(unix)]
impl From<PathBuf> for Addr {
fn from(s: PathBuf) -> Addr {
Addr::Unix(s)
}
}
#[cfg(unix)]
impl From<unix::SocketAddr> for Addr {
fn from(s: unix::SocketAddr) -> Addr {
Addr::Unix(match s.as_pathname() {
None => Path::new("unnamed").to_path_buf(),
Some(p) => p.to_path_buf(),
})
}
}
#[cfg(unix)]
impl From<tokio::net::unix::SocketAddr> for Addr {
fn from(s: tokio::net::unix::SocketAddr) -> Addr {
Addr::Unix(match s.as_pathname() {
None => Path::new("unnamed").to_path_buf(),
Some(p) => p.to_path_buf(),
})
}
}
impl fmt::Display for Addr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Addr::Inet(n) => n.fmt(f),
#[cfg(unix)]
Addr::Unix(n) => n.to_string_lossy().fmt(f),
}
}
}
impl FromStr for Addr {
type Err = net::AddrParseError;
fn from_str(v: &str) -> Result<Self, Self::Err> {
#[cfg(unix)]
if v.starts_with('/') || v.starts_with("./") {
return Ok(Addr::Unix(PathBuf::from(v)));
}
Ok(Addr::Inet(v.parse()?))
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> serde::de::Deserialize<'de> for Addr {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = Addr;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a Socket Address")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Addr::from_str(v).map_err(E::custom)
}
}
deserializer.deserialize_str(Visitor)
}
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
#[test]
fn parse_addr() {
if let Ok(Addr::Inet(net::SocketAddr::V4(f))) = Addr::from_str("127.0.0.1:9000") {
assert!(f.ip().is_loopback());
assert_eq!(f.port(), 9000);
}
if let Ok(Addr::Inet(f)) = Addr::from_str("localhost:9000") {
assert_eq!(f.port(), 9000);
}
if let Ok(Addr::Inet(net::SocketAddr::V6(f))) = Addr::from_str("[::1]:9000") {
assert!(f.ip().is_loopback());
assert_eq!(f.port(), 9000);
}
#[cfg(unix)]
if let Ok(Addr::Unix(f)) = Addr::from_str("/path") {
assert_eq!(f.as_os_str(), "/path");
}
}
#[test]
fn display() {
assert_eq!(
"127.0.0.1:1234",
Addr::Inet(net::SocketAddr::V4(net::SocketAddrV4::new(
net::Ipv4Addr::new(127, 0, 0, 1),
1234
)))
.to_string()
);
#[cfg(unix)]
assert_eq!(
"/tmp/bla",
Addr::Unix(PathBuf::from_str("/tmp/bla").unwrap()).to_string()
);
}
}