1use crate::host::{AddressParseError, HostAddress};
2use crate::socket::SocketAddress;
3use serde::{Deserialize, Serialize};
4use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
5use std::os::unix::net::SocketAddr as UnixSocketAddr;
6use std::str::FromStr;
7
8#[derive(Clone, Serialize, Deserialize)]
11#[serde(try_from = "String", into = "String")]
12pub enum HostOrSocketAddress {
13 UnixDomain(Box<UnixSocketAddr>),
14 V4Socket(Box<SocketAddrV4>),
15 V6Socket(Box<SocketAddrV6>),
16 V4Host(std::net::Ipv4Addr),
17 V6Host(std::net::Ipv6Addr),
18}
19
20impl std::fmt::Debug for HostOrSocketAddress {
21 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
22 <Self as std::fmt::Display>::fmt(self, fmt)
23 }
24}
25
26impl std::fmt::Display for HostOrSocketAddress {
27 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
28 match self {
29 Self::UnixDomain(unix) => match unix.as_pathname() {
30 Some(path) => path.display().fmt(fmt),
31 None => write!(fmt, "<unbound unix domain>"),
32 },
33 Self::V4Socket(a) => a.fmt(fmt),
34 Self::V6Socket(a) => a.fmt(fmt),
35 Self::V4Host(a) => a.fmt(fmt),
36 Self::V6Host(a) => a.fmt(fmt),
37 }
38 }
39}
40
41impl From<SocketAddress> for HostOrSocketAddress {
42 fn from(a: SocketAddress) -> HostOrSocketAddress {
43 match a {
44 SocketAddress::UnixDomain(unix) => HostOrSocketAddress::UnixDomain(unix),
45 SocketAddress::V4(v4) => HostOrSocketAddress::V4Socket(v4.into()),
46 SocketAddress::V6(v6) => HostOrSocketAddress::V6Socket(v6.into()),
47 }
48 }
49}
50
51impl From<HostAddress> for HostOrSocketAddress {
52 fn from(a: HostAddress) -> HostOrSocketAddress {
53 match a {
54 HostAddress::UnixDomain(unix) => HostOrSocketAddress::UnixDomain(unix),
55 HostAddress::V4(v4) => HostOrSocketAddress::V4Host(v4),
56 HostAddress::V6(v6) => HostOrSocketAddress::V6Host(v6),
57 }
58 }
59}
60
61impl From<HostOrSocketAddress> for String {
62 fn from(a: HostOrSocketAddress) -> String {
63 format!("{a}")
64 }
65}
66
67impl TryFrom<String> for HostOrSocketAddress {
68 type Error = AddressParseError;
69 fn try_from(s: String) -> Result<HostOrSocketAddress, Self::Error> {
70 HostOrSocketAddress::from_str(&s)
71 }
72}
73
74impl HostOrSocketAddress {
75 pub fn host(&self) -> HostAddress {
77 match self {
78 Self::UnixDomain(p) => HostAddress::UnixDomain(p.clone()),
79 Self::V4Host(a) => HostAddress::V4(*a),
80 Self::V4Socket(a) => HostAddress::V4(*a.ip()),
81 Self::V6Host(a) => HostAddress::V6(*a),
82 Self::V6Socket(a) => HostAddress::V6(*a.ip()),
83 }
84 }
85
86 pub fn unix(&self) -> Option<UnixSocketAddr> {
88 match self {
89 Self::UnixDomain(unix) => Some((**unix).clone()),
90 Self::V4Host(_) | Self::V6Host(_) | Self::V4Socket(_) | Self::V6Socket(_) => None,
91 }
92 }
93
94 pub fn ip(&self) -> Option<IpAddr> {
96 match self {
97 Self::UnixDomain(_) => None,
98 Self::V4Host(a) => Some((*a).into()),
99 Self::V4Socket(a) => Some((*a.ip()).into()),
100 Self::V6Host(a) => Some((*a).into()),
101 Self::V6Socket(a) => Some((*a.ip()).into()),
102 }
103 }
104
105 pub fn ip_and_port(&self) -> Option<SocketAddr> {
107 match self {
108 Self::UnixDomain(_) => None,
109 Self::V4Host(_) | Self::V6Host(_) => None,
110 Self::V4Socket(a) => Some((*a.clone()).into()),
111 Self::V6Socket(a) => Some((*a.clone()).into()),
112 }
113 }
114
115 pub fn port(&self) -> Option<u16> {
117 self.ip_and_port().map(|sa| sa.port())
118 }
119
120 pub fn set_port(&mut self, port: u16) {
122 match self {
123 HostOrSocketAddress::UnixDomain(_) => {}
124 HostOrSocketAddress::V6Socket(s) => {
125 s.set_port(port);
126 }
127 HostOrSocketAddress::V4Socket(s) => {
128 s.set_port(port);
129 }
130 HostOrSocketAddress::V4Host(v4) => {
131 *self = HostOrSocketAddress::V4Socket(Box::new(SocketAddrV4::new(*v4, port)));
132 }
133 HostOrSocketAddress::V6Host(v6) => {
134 *self = HostOrSocketAddress::V6Socket(Box::new(SocketAddrV6::new(*v6, port, 0, 0)));
135 }
136 }
137 }
138
139 pub fn set_port_if_not_set(&mut self, port: u16) {
141 match self {
142 HostOrSocketAddress::UnixDomain(_) => {}
143 HostOrSocketAddress::V6Socket(_) | HostOrSocketAddress::V4Socket(_) => {
144 }
146 HostOrSocketAddress::V4Host(v4) => {
147 *self = HostOrSocketAddress::V4Socket(Box::new(SocketAddrV4::new(*v4, port)));
148 }
149 HostOrSocketAddress::V6Host(v6) => {
150 *self = HostOrSocketAddress::V6Socket(Box::new(SocketAddrV6::new(*v6, port, 0, 0)));
151 }
152 }
153 }
154}
155
156impl FromStr for HostOrSocketAddress {
157 type Err = AddressParseError;
158 fn from_str(s: &str) -> Result<HostOrSocketAddress, Self::Err> {
159 match SocketAddress::from_str(s) {
160 Ok(sa) => Ok(sa.into()),
161 Err(sa_err) => match HostAddress::from_str(s) {
162 Ok(ha) => Ok(ha.into()),
163 Err(_ha_err) => Err(sa_err),
164 },
165 }
166 }
167}
168
169impl PartialEq for HostOrSocketAddress {
170 fn eq(&self, other: &Self) -> bool {
171 match (self, other) {
172 (Self::UnixDomain(a), Self::UnixDomain(b)) => {
173 match (a.as_pathname(), b.as_pathname()) {
174 (Some(a), Some(b)) => a.eq(b),
175 (None, None) => true,
176 _ => false,
177 }
178 }
179 (Self::V4Socket(a), Self::V4Socket(b)) => a.eq(b),
180 (Self::V6Socket(a), Self::V6Socket(b)) => a.eq(b),
181 (Self::V4Host(a), Self::V4Host(b)) => a.eq(b),
182 (Self::V6Host(a), Self::V6Host(b)) => a.eq(b),
183 _ => false,
184 }
185 }
186}
187
188impl Eq for HostOrSocketAddress {}
189
190impl From<UnixSocketAddr> for HostOrSocketAddress {
191 fn from(unix: UnixSocketAddr) -> HostOrSocketAddress {
192 HostOrSocketAddress::UnixDomain(unix.into())
193 }
194}
195
196impl From<IpAddr> for HostOrSocketAddress {
197 fn from(ip: IpAddr) -> HostOrSocketAddress {
198 match ip {
199 IpAddr::V4(a) => HostOrSocketAddress::V4Host(a),
200 IpAddr::V6(a) => HostOrSocketAddress::V6Host(a),
201 }
202 }
203}
204
205impl From<Ipv4Addr> for HostOrSocketAddress {
206 fn from(ip: Ipv4Addr) -> HostOrSocketAddress {
207 HostOrSocketAddress::V4Host(ip)
208 }
209}
210
211impl From<Ipv6Addr> for HostOrSocketAddress {
212 fn from(ip: Ipv6Addr) -> HostOrSocketAddress {
213 HostOrSocketAddress::V6Host(ip)
214 }
215}
216
217impl From<SocketAddr> for HostOrSocketAddress {
218 fn from(ip: SocketAddr) -> HostOrSocketAddress {
219 match ip {
220 SocketAddr::V4(a) => HostOrSocketAddress::V4Socket(a.into()),
221 SocketAddr::V6(a) => HostOrSocketAddress::V6Socket(a.into()),
222 }
223 }
224}
225
226impl From<tokio::net::unix::SocketAddr> for HostOrSocketAddress {
227 fn from(unix: tokio::net::unix::SocketAddr) -> HostOrSocketAddress {
228 let unix: UnixSocketAddr = unix.into();
229 unix.into()
230 }
231}
232
233#[cfg(test)]
234mod test {
235 use super::*;
236 use std::net::{Ipv4Addr, Ipv6Addr};
237
238 #[test]
239 fn parse() {
240 assert_eq!(
241 "10.0.0.1:25".parse::<HostOrSocketAddress>(),
242 Ok(HostOrSocketAddress::V4Socket(
243 SocketAddrV4::new(Ipv4Addr::new(10, 0, 0, 1), 25).into()
244 ))
245 );
246 assert_eq!(
247 "[10.0.0.1]:25".parse::<HostOrSocketAddress>(),
248 Ok(HostOrSocketAddress::V4Socket(
249 SocketAddrV4::new(Ipv4Addr::new(10, 0, 0, 1), 25).into()
250 ))
251 );
252 assert_eq!(
253 "[::1]:100".parse::<HostOrSocketAddress>(),
254 Ok(HostOrSocketAddress::V6Socket(
255 SocketAddrV6::new(Ipv6Addr::LOCALHOST, 100, 0, 0).into()
256 ))
257 );
258 assert_eq!(
259 "/some/path".parse::<HostOrSocketAddress>(),
260 Ok(HostOrSocketAddress::UnixDomain(
261 UnixSocketAddr::from_pathname("/some/path").unwrap().into()
262 ))
263 );
264 assert_eq!(
265 "[10.0.0.1]".parse::<HostOrSocketAddress>(),
266 Ok(HostOrSocketAddress::V4Host(Ipv4Addr::new(10, 0, 0, 1),))
267 );
268 assert_eq!(
269 "[::1]".parse::<HostOrSocketAddress>(),
270 Ok(HostOrSocketAddress::V6Host(Ipv6Addr::LOCALHOST))
271 );
272 assert_eq!(
273 format!(
274 "{:#}",
275 "hello there".parse::<HostOrSocketAddress>().unwrap_err()
276 ),
277 "Failed to parse 'hello there' as an address. \
278 Got 'invalid socket address syntax' when considering it as \
279 an IP address and 'unix domain path must be absolute' \
280 when considering it as a unix domain socket path."
281 );
282 assert_eq!(
283 format!(
284 "{:#}",
285 "[10.0.0.1]:bogus"
286 .parse::<HostOrSocketAddress>()
287 .unwrap_err()
288 ),
289 "Failed to parse '[10.0.0.1]:bogus' as an address. \
290 Got 'invalid socket address syntax' when considering it as \
291 an IP address and 'unix domain path must be absolute' \
292 when considering it as a unix domain socket path."
293 );
294 }
295}