1use std::{fmt, io};
4
5pub mod listener;
6#[cfg(unix)]
7pub mod unix;
8
9pub const UNIX_URI_PREFIX: &str = "unix://";
14
15#[derive(Clone, PartialEq, Eq, Hash)]
16pub enum SocketAddr {
26 Inet(std::net::SocketAddr),
28
29 #[cfg(unix)]
30 Unix(unix::SocketAddr),
32}
33
34impl fmt::Debug for SocketAddr {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 match self {
37 SocketAddr::Inet(addr) => addr.fmt(f),
38 #[cfg(unix)]
39 SocketAddr::Unix(addr) => addr.fmt(f),
40 }
41 }
42}
43
44impl From<std::net::SocketAddr> for SocketAddr {
45 fn from(addr: std::net::SocketAddr) -> Self {
46 SocketAddr::Inet(addr)
47 }
48}
49
50#[cfg(unix)]
51impl From<unix::SocketAddr> for SocketAddr {
52 fn from(addr: unix::SocketAddr) -> Self {
53 SocketAddr::Unix(addr)
54 }
55}
56
57#[cfg(all(unix, feature = "feat-tokio"))]
58impl From<tokio::net::unix::SocketAddr> for SocketAddr {
59 fn from(addr: tokio::net::unix::SocketAddr) -> Self {
60 SocketAddr::Unix(unix::SocketAddr::from(addr.into()))
61 }
62}
63
64impl SocketAddr {
65 #[inline]
66 pub fn new(addr: &str) -> io::Result<Self> {
90 if let Some(addr) = addr.strip_prefix(UNIX_URI_PREFIX) {
91 #[cfg(unix)]
92 return unix::SocketAddr::new(addr).map(SocketAddr::Unix);
93
94 #[cfg(not(unix))]
95 return Err(io::Error::new(
96 io::ErrorKind::Unsupported,
97 "Unix socket addresses are not supported on this platform",
98 ));
99 }
100
101 addr.parse()
102 .map(SocketAddr::Inet)
103 .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "unknown format"))
104 }
105
106 #[inline]
107 pub fn bind_std(&self) -> io::Result<listener::StdListener> {
109 match self {
110 SocketAddr::Inet(addr) => std::net::TcpListener::bind(addr).map(listener::StdListener::Tcp),
111 #[cfg(unix)]
112 SocketAddr::Unix(addr) => addr.bind_std().map(listener::StdListener::Unix),
113 }
114 }
115
116 #[cfg(feature = "feat-tokio")]
117 #[inline]
118 pub async fn bind(&self) -> io::Result<listener::Listener> {
120 match self {
121 SocketAddr::Inet(addr) => tokio::net::TcpListener::bind(addr).await.map(listener::Listener::Tcp),
122 #[cfg(unix)]
123 SocketAddr::Unix(addr) => addr.bind().map(listener::Listener::Unix),
124 }
125 }
126
127 pub fn to_string_ext(&self) -> Option<String> {
129 match self {
130 Self::Inet(addr) => Some(addr.to_string()),
131 Self::Unix(addr) => addr._to_os_string(UNIX_URI_PREFIX, "@").into_string().ok(),
132 }
133 }
134}
135
136#[cfg(feature = "feat-serde")]
137impl serde::Serialize for SocketAddr {
138 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
139 where
140 S: serde::Serializer,
141 {
142 serializer.serialize_str(
143 &self
144 .to_string_ext()
145 .ok_or_else(|| serde::ser::Error::custom("invalid UTF-8"))?,
146 )
147 }
148}
149
150#[cfg(feature = "feat-serde")]
151impl<'de> serde::Deserialize<'de> for SocketAddr {
152 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
153 where
154 D: serde::Deserializer<'de>,
155 {
156 Self::new(<&str>::deserialize(deserializer)?).map_err(serde::de::Error::custom)
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 #[cfg(unix)]
163 use std::os::linux::net::SocketAddrExt;
164
165 use super::*;
166
167 #[test]
168 fn test_socket_addr_new_ipv4() {
169 let addr = SocketAddr::new("127.0.0.1:8080").unwrap();
170
171 match addr {
172 SocketAddr::Inet(std_addr) => {
173 assert_eq!(std_addr.ip().to_string(), "127.0.0.1");
174 assert_eq!(std_addr.port(), 8080);
175 }
176 #[cfg(unix)]
177 SocketAddr::Unix(_) => unreachable!(),
178 }
179 }
180
181 #[test]
182 fn test_socket_addr_new_ipv6() {
183 let addr = SocketAddr::new("[::1]:8080").unwrap();
184
185 match addr {
186 SocketAddr::Inet(std_addr) => {
187 assert_eq!(std_addr.ip().to_string(), "::1");
188 assert_eq!(std_addr.port(), 8080);
189 }
190 #[cfg(unix)]
191 SocketAddr::Unix(_) => unreachable!(),
192 }
193 }
194
195 #[cfg(unix)]
196 #[test]
197 fn test_socket_addr_new_unix_pathname() {
198 let addr = SocketAddr::new("unix:///tmp/test.sock").unwrap();
199
200 match addr {
201 SocketAddr::Inet(_) => unreachable!(),
202 SocketAddr::Unix(unix_addr) => {
203 assert!(unix_addr.as_pathname().is_some());
204 }
205 }
206 }
207
208 #[cfg(unix)]
209 #[test]
210 fn test_socket_addr_new_unix_abstract() {
211 let addr = SocketAddr::new("unix://@test.abstract").unwrap();
212
213 match addr {
214 SocketAddr::Inet(_) => unreachable!(),
215 SocketAddr::Unix(unix_addr) => {
216 assert!(unix_addr.as_abstract_name().is_some());
217 }
218 }
219 }
220
221 #[test]
222 fn test_socket_addr_new_invalid() {
223 assert!(SocketAddr::new("invalid").is_err());
225 assert!(SocketAddr::new("127.0.0.1").is_err()); assert!(SocketAddr::new("127.0.0.1:invalid").is_err()); }
228
229 #[cfg(not(unix))]
230 #[test]
231 fn test_socket_addr_new_unix_unsupported() {
232 let result = SocketAddr::new("unix:///tmp/test.sock");
234
235 assert_eq!(result.unwrap_err().kind(), io::ErrorKind::Unsupported);
236 }
237
238 #[test]
239 fn test_socket_addr_display() {
240 let addr = SocketAddr::new("127.0.0.1:8080").unwrap();
241 assert_eq!(&addr.to_string_ext().unwrap(), "127.0.0.1:8080");
242
243 let addr = SocketAddr::new("[::1]:8080").unwrap();
244 assert_eq!(&addr.to_string_ext().unwrap(), "[::1]:8080");
245
246 #[cfg(unix)]
247 {
248 let addr = SocketAddr::new("unix:///tmp/test.sock").unwrap();
249 assert_eq!(&addr.to_string_ext().unwrap(), "unix:///tmp/test.sock");
250
251 let addr = SocketAddr::new("unix://@test.abstract").unwrap();
252 assert_eq!(&addr.to_string_ext().unwrap(), "unix://@test.abstract");
253 }
254 }
255
256 #[test]
257 fn test_socket_addr_debug() {
258 let addr = SocketAddr::new("127.0.0.1:8080").unwrap();
259 let debug_str = format!("{:?}", addr);
260
261 assert!(debug_str.contains("127.0.0.1:8080"));
262 }
263
264 #[test]
265 fn test_bind_std() {
266 let addr = SocketAddr::new("127.0.0.1:0").unwrap();
267 let _listener = addr.bind_std().unwrap();
268 }
269
270 #[cfg(feature = "feat-tokio")]
271 #[tokio::test]
272 async fn test_bind_tokio() {
273 let addr = SocketAddr::new("127.0.0.1:0").unwrap();
274 let _listener = addr.bind().await.unwrap();
275 }
276
277 #[cfg(all(unix, feature = "feat-tokio"))]
278 #[tokio::test]
279 async fn test_bind_tokio_unix() {
280 let addr = SocketAddr::new("unix:///tmp/test_bind_tokio_unix.sock").unwrap();
281 let _listener = addr.bind().await.unwrap();
282 }
283
284 #[cfg(all(any(target_os = "android", target_os = "linux"), feature = "feat-tokio"))]
285 #[tokio::test]
286 async fn test_bind_tokio_unix_abstract() {
287 let addr = SocketAddr::new("unix://@abstract.test").unwrap();
288 let _listener = addr.bind().await.unwrap();
289 }
290
291 #[test]
292 fn test_edge_cases() {
293 assert!(SocketAddr::new("").is_err());
294 assert!(SocketAddr::new("not-an-address").is_err());
295 assert!(SocketAddr::new("127.0.0.1:99999").is_err()); #[cfg(unix)]
298 {
299 assert!(SocketAddr::new("unix://").is_ok()); #[cfg(any(target_os = "android", target_os = "linux"))]
301 assert!(SocketAddr::new("unix://@").is_ok()); }
303 }
304}