1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
use std::fmt; use std::str::FromStr; use std::path::{Path, PathBuf}; use async_std::net; #[cfg(unix)] use async_std::os::unix::net as unix; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum SocketAddr { Inet(net::SocketAddr), #[cfg(unix)] Unix(PathBuf) } impl From<net::SocketAddr> for SocketAddr { fn from(s: net::SocketAddr) -> SocketAddr { SocketAddr::Inet(s) } } #[cfg(unix)] impl From<unix::SocketAddr> for SocketAddr { fn from(s: unix::SocketAddr) -> SocketAddr { SocketAddr::Unix(match s.as_pathname() { None => Path::new(".sock").to_path_buf(), Some(p) => p.to_path_buf() }) } } impl fmt::Display for SocketAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { SocketAddr::Inet(n) => write!(f, "{}", n), #[cfg(unix)] SocketAddr::Unix(n) => write!(f, "unix:{}", n.to_string_lossy()) } } } impl FromStr for SocketAddr { type Err = net::AddrParseError; #[cfg(unix)] fn from_str(s: &str) -> Result<SocketAddr, net::AddrParseError> { if s.starts_with("unix:") { Ok(SocketAddr::Unix(Path::new(s.trim_start_matches("unix:")).to_path_buf())) } else { s.parse().map(SocketAddr::Inet) } } #[cfg(not(unix))] fn from_str(s: &str) -> Result<SocketAddr, net::AddrParseError> { s.parse().map(SocketAddr::Inet) } } impl SocketAddr { pub fn from_str<S: Into<String>>(txt: S) -> Result<Self, ()> { let txt = txt.into(); if txt.starts_with("unix:") { let addr = match txt.parse::<Self>() { Ok(addr) => addr, Err(_) => return Err(()), }; Ok(Self::from(addr)) } else { let addr = match txt.parse::<net::SocketAddr>() { Ok(addr) => addr, Err(_) => return Err(()), }; Ok(Self::from(addr)) } } pub fn is_unix(&self) -> bool { match self { #[cfg(unix)] SocketAddr::Unix(_) => true, _ => false, } } pub fn is_inet(&self) -> bool { !self.is_unix() } } #[cfg(test)] mod tests { use super::*; #[async_std::test] async fn creates_from_inet() { let ip4 = SocketAddr::from_str("127.0.0.1:10"); let ip6 = SocketAddr::from_str("[::20]:10"); let invalid = SocketAddr::from_str("foo"); assert!(ip4.is_ok()); assert!(ip6.is_ok()); assert!(invalid.is_err()); assert_eq!(ip4.unwrap().to_string(), "127.0.0.1:10"); assert_eq!(ip6.unwrap().to_string(), "[::0.0.0.32]:10"); } #[async_std::test] #[cfg(unix)] async fn creates_from_unix() { let unix = SocketAddr::from_str("unix:/tmp/sock"); let invalid = SocketAddr::from_str("/tmp/sock"); assert!(unix.is_ok()); assert!(invalid.is_err()); } }