Skip to main content

rsntp/
to_server_addrs.rs

1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
2
3/// A trait for objects which can be converted or resolved to one or more SocketAddr values with or
4/// without a port.
5///
6/// This trait is similar to the `std::net::ToSocketAddrs` trait, but the port is optional.
7/// If no port is supplied then the user of this trait falls back to a default one.
8///
9/// It is implemented for almost all types `ToSocketAddrs` is implemented for, plus for some addition types
10/// where the optional port is not present (`(IpAddr, u16)`, and also for simple `IpAddr`). For string
11/// implementation, the port is also optional, so both `<host>:<port>` and `<host>` can be used.
12///
13/// The trait is intended to be opaque; details might change.
14///
15/// # Examples
16///
17/// ```no_run
18/// use rsntp::ToServerAddrs;
19/// use std::net::Ipv4Addr;
20///
21/// fn connect<A: ToServerAddrs>(addr: A) {
22///   // connect to a server with default port 1234
23/// }
24///
25/// connect("127.0.0.1"); // will connect to 127.0.0.1:1234
26/// connect("127.0.0.1:456"); // will connect to 127.0.0.1:456
27///
28/// connect(Ipv4Addr::new(127, 0, 0, 1)); // will connect to 127.0.0.1:1234
29/// connect((Ipv4Addr::new(127, 0, 0, 1), 456)); // will connect to 127.0.0.1:456
30///
31/// ```
32pub trait ToServerAddrs {
33    #[cfg(feature = "async")]
34    #[doc(hidden)]
35    type Return: std::net::ToSocketAddrs + tokio::net::ToSocketAddrs;
36    #[cfg(not(feature = "async"))]
37    #[doc(hidden)]
38    type Return: std::net::ToSocketAddrs;
39
40    #[doc(hidden)]
41    fn to_server_addrs(&self, default_port: u16) -> Self::Return;
42}
43
44impl ToServerAddrs for SocketAddr {
45    type Return = SocketAddr;
46
47    fn to_server_addrs(&self, _default_port: u16) -> Self::Return {
48        *self
49    }
50}
51
52impl ToServerAddrs for SocketAddrV4 {
53    type Return = SocketAddrV4;
54
55    fn to_server_addrs(&self, _default_port: u16) -> Self::Return {
56        *self
57    }
58}
59
60impl ToServerAddrs for SocketAddrV6 {
61    type Return = SocketAddrV6;
62
63    fn to_server_addrs(&self, _default_port: u16) -> Self::Return {
64        *self
65    }
66}
67
68impl ToServerAddrs for IpAddr {
69    type Return = (IpAddr, u16);
70
71    fn to_server_addrs(&self, default_port: u16) -> Self::Return {
72        (*self, default_port)
73    }
74}
75
76impl ToServerAddrs for (IpAddr, u16) {
77    type Return = (IpAddr, u16);
78
79    fn to_server_addrs(&self, _default_port: u16) -> Self::Return {
80        *self
81    }
82}
83
84impl ToServerAddrs for Ipv4Addr {
85    type Return = (Ipv4Addr, u16);
86
87    fn to_server_addrs(&self, default_port: u16) -> Self::Return {
88        (*self, default_port)
89    }
90}
91
92impl ToServerAddrs for (Ipv4Addr, u16) {
93    type Return = (Ipv4Addr, u16);
94
95    fn to_server_addrs(&self, _default_port: u16) -> Self::Return {
96        *self
97    }
98}
99
100impl ToServerAddrs for Ipv6Addr {
101    type Return = (Ipv6Addr, u16);
102
103    fn to_server_addrs(&self, default_port: u16) -> Self::Return {
104        (*self, default_port)
105    }
106}
107
108impl ToServerAddrs for (Ipv6Addr, u16) {
109    type Return = (Ipv6Addr, u16);
110
111    fn to_server_addrs(&self, _default_port: u16) -> Self::Return {
112        *self
113    }
114}
115
116impl ToServerAddrs for str {
117    type Return = String;
118
119    fn to_server_addrs(&self, default_port: u16) -> Self::Return {
120        if self.parse::<Ipv4Addr>().is_ok() {
121            self.to_string() + ":" + &default_port.to_string()
122        } else if self.parse::<Ipv6Addr>().is_ok() {
123            "[".to_string() + self + "]:" + &default_port.to_string()
124        } else if self.starts_with("[") && self.ends_with("]") {
125            // probably an IPv6 address between [ and ]
126            self.to_string() + ":" + &default_port.to_string()
127        } else if !self.contains(":") {
128            self.to_string() + ":" + &default_port.to_string()
129        } else {
130            self.to_string()
131        }
132    }
133}
134
135impl ToServerAddrs for String {
136    type Return = String;
137
138    fn to_server_addrs(&self, default_port: u16) -> Self::Return {
139        (**self).to_server_addrs(default_port)
140    }
141}
142
143impl<'a> ToServerAddrs for (&'a str, u16) {
144    type Return = (&'a str, u16);
145
146    fn to_server_addrs(&self, _default_port: u16) -> Self::Return {
147        *self
148    }
149}
150
151impl ToServerAddrs for (String, u16) {
152    type Return = String;
153
154    fn to_server_addrs(&self, _default_port: u16) -> Self::Return {
155        self.0.clone() + ":" + &self.1.to_string()
156    }
157}
158
159impl<T: ToServerAddrs + ?Sized> ToServerAddrs for &T {
160    type Return = T::Return;
161
162    fn to_server_addrs(&self, default_port: u16) -> Self::Return {
163        (**self).to_server_addrs(default_port)
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170
171    #[test]
172    fn works_for_socket_addr() {
173        let s = SocketAddr::from(([127, 0, 0, 1], 1234));
174        let s4 = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1234);
175        let s6 = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 1234, 0, 0);
176
177        assert_eq!(s.to_server_addrs(456), s);
178        assert_eq!(s4.to_server_addrs(456), s4);
179        assert_eq!(s6.to_server_addrs(456), s6);
180    }
181
182    #[test]
183    fn works_for_ip_addr() {
184        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
185        let ip4 = Ipv4Addr::new(127, 0, 0, 1);
186        let ip6 = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
187
188        assert_eq!(ip.to_server_addrs(456), (ip, 456));
189        assert_eq!(ip4.to_server_addrs(456), (ip4, 456));
190        assert_eq!(ip6.to_server_addrs(456), (ip6, 456));
191
192        assert_eq!((ip, 1234).to_server_addrs(456), (ip, 1234));
193        assert_eq!((ip4, 1234).to_server_addrs(456), (ip4, 1234));
194        assert_eq!((ip6, 1234).to_server_addrs(456), (ip6, 1234));
195    }
196
197    #[test]
198    fn works_for_ipv4_strings() {
199        assert_eq!("127.0.0.1".to_server_addrs(456), "127.0.0.1:456");
200        assert_eq!("127.0.0.1:1234".to_server_addrs(456), "127.0.0.1:1234");
201        assert_eq!(
202            "127.0.0.1".to_string().to_server_addrs(456),
203            "127.0.0.1:456"
204        );
205        assert_eq!(
206            "127.0.0.1:1234".to_string().to_server_addrs(456),
207            "127.0.0.1:1234"
208        );
209
210        assert_eq!(
211            ("127.0.0.1", 1234).to_server_addrs(456),
212            ("127.0.0.1", 1234)
213        );
214
215        assert_eq!(
216            ("127.0.0.1".to_string(), 1234).to_server_addrs(456),
217            "127.0.0.1:1234"
218        );
219    }
220
221    #[test]
222    fn works_for_ip6_string() {
223        assert_eq!("::1".to_server_addrs(456), "[::1]:456");
224        assert_eq!("[::1]".to_server_addrs(456), "[::1]:456");
225        assert_eq!("[::1]:1234".to_server_addrs(456), "[::1]:1234");
226
227        assert_eq!("::1".to_string().to_server_addrs(456), "[::1]:456");
228        assert_eq!("[::1]".to_string().to_server_addrs(456), "[::1]:456");
229        assert_eq!("[::1]:1234".to_string().to_server_addrs(456), "[::1]:1234");
230    }
231}