hostaddr/
addr.rs

1use super::{Domain, Host, ParseAsciiHostError, ParseHostError};
2
3use core::{
4  net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
5  str::FromStr,
6};
7
8/// An error which can be returned when parsing a [`HostAddr`].
9#[derive(Debug, thiserror::Error)]
10pub enum ParseHostAddrError {
11  /// Returned if the provided str does not contains a valid host.
12  #[error(transparent)]
13  Host(#[from] ParseHostError),
14  /// Returned if the provided str does not contains a valid port.
15  #[error(transparent)]
16  Port(#[from] core::num::ParseIntError),
17}
18
19impl ParseHostAddrError {
20  #[inline]
21  const fn host() -> Self {
22    Self::Host(ParseHostError(()))
23  }
24}
25
26/// An error which can be returned when parsing a [`HostAddr`].
27#[derive(Debug, thiserror::Error)]
28pub enum ParseAsciiHostAddrError {
29  /// Returned if the provided str does not contains a valid host.
30  #[error(transparent)]
31  Host(#[from] ParseAsciiHostError),
32  /// Returned if the provided str does not contains a valid port.
33  #[error(transparent)]
34  Port(#[from] core::num::ParseIntError),
35}
36
37impl ParseAsciiHostAddrError {
38  #[inline]
39  const fn host() -> Self {
40    Self::Host(ParseAsciiHostError(()))
41  }
42}
43
44/// A host address, which is consit of a [`Host`] and an optional port number.
45#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub struct HostAddr<S> {
48  /// The host name
49  pub(super) host: Host<S>,
50  /// The port number
51  pub(super) port: Option<u16>,
52}
53
54#[cfg(feature = "cheap-clone")]
55impl<S: cheap_clone::CheapClone> cheap_clone::CheapClone for HostAddr<S> {}
56
57impl<S> core::fmt::Display for HostAddr<S>
58where
59  S: core::fmt::Display,
60{
61  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62    match self.port {
63      Some(port) => write!(f, "{}:{}", self.host, port),
64      None => write!(f, "{}", self.host),
65    }
66  }
67}
68
69impl<S> From<Domain<S>> for HostAddr<S> {
70  /// ```rust
71  /// # #[cfg(any(feature = "std", feature = "alloc"))]
72  /// # {
73  /// use hostaddr::{Domain, HostAddr};
74  ///
75  /// let domain = Domain::<String>::try_from("example.com").unwrap();
76  /// let host = HostAddr::<String>::from(domain);
77  /// # }
78  /// ```
79  fn from(domain: Domain<S>) -> Self {
80    Self::from_domain(domain)
81  }
82}
83
84impl<S> From<(Domain<S>, u16)> for HostAddr<S> {
85  /// ```rust
86  /// # #[cfg(any(feature = "std", feature = "alloc"))]
87  /// # {
88  /// use hostaddr::{Domain, HostAddr};
89  ///
90  /// let domain = Domain::<String>::try_from("example.com").unwrap();
91  /// let host = HostAddr::<String>::from((domain, 8080));
92  /// # }
93  /// ```
94  fn from((host, port): (Domain<S>, u16)) -> Self {
95    Self::from_domain(host).with_port(port)
96  }
97}
98
99impl<S> From<(u16, Domain<S>)> for HostAddr<S> {
100  /// ```rust
101  /// # #[cfg(any(feature = "std", feature = "alloc"))]
102  /// # {
103  /// use hostaddr::{Domain, HostAddr};
104  ///
105  /// let domain = Domain::<String>::try_from("example.com").unwrap();
106  /// let host = HostAddr::<String>::from((8080, domain));
107  /// # }
108  /// ```
109  fn from((port, host): (u16, Domain<S>)) -> Self {
110    Self::from_domain(host).with_port(port)
111  }
112}
113
114impl<S> From<(IpAddr, u16)> for HostAddr<S> {
115  /// ```rust
116  /// # #[cfg(any(feature = "std", feature = "alloc"))]
117  /// # {
118  /// use hostaddr::HostAddr;
119  /// use std::net::IpAddr;
120  ///
121  /// let ip = "127.0.0.1".parse::<IpAddr>().unwrap();
122  /// let addr = HostAddr::<String>::from((ip, 8080));
123  /// # }
124  /// ```
125  fn from((host, port): (IpAddr, u16)) -> Self {
126    Self::from_ip_addr(host).with_port(port)
127  }
128}
129
130impl<S> From<(u16, IpAddr)> for HostAddr<S> {
131  /// ```rust
132  /// # #[cfg(any(feature = "std", feature = "alloc"))]
133  /// # {
134  /// use hostaddr::HostAddr;
135  /// use std::net::IpAddr;
136  ///
137  /// let ip = "::1".parse::<IpAddr>().unwrap();
138  /// let addr = HostAddr::<String>::from((8080, ip));
139  /// # }
140  /// ```
141  fn from((port, host): (u16, IpAddr)) -> Self {
142    Self::from_ip_addr(host).with_port(port)
143  }
144}
145
146impl<S> From<(Ipv4Addr, u16)> for HostAddr<S> {
147  /// ```rust
148  /// # #[cfg(any(feature = "std", feature = "alloc"))]
149  /// # {
150  /// use hostaddr::HostAddr;
151  /// use std::net::Ipv4Addr;
152  ///
153  /// let ip = "127.0.0.1".parse::<Ipv4Addr>().unwrap();
154  /// let addr = HostAddr::<String>::from((ip, 8080));
155  /// # }
156  /// ```
157  fn from((host, port): (Ipv4Addr, u16)) -> Self {
158    Self::from((IpAddr::V4(host), port))
159  }
160}
161
162impl<S> From<(Ipv6Addr, u16)> for HostAddr<S> {
163  /// ```rust
164  /// # #[cfg(any(feature = "std", feature = "alloc"))]
165  /// # {
166  /// use hostaddr::HostAddr;
167  /// use std::net::Ipv6Addr;
168  ///
169  /// let ip = "::1".parse::<Ipv6Addr>().unwrap();
170  /// let addr = HostAddr::<String>::from((ip, 8080));
171  /// # }
172  /// ```
173  fn from((host, port): (Ipv6Addr, u16)) -> Self {
174    Self::from((port, IpAddr::V6(host)))
175  }
176}
177
178impl<S> From<SocketAddr> for HostAddr<S> {
179  /// ```rust
180  /// # #[cfg(any(feature = "std", feature = "alloc"))]
181  /// # {
182  /// use hostaddr::HostAddr;
183  /// use std::net::SocketAddr;
184  ///
185  /// let addr = "127.0.0.1:8080".parse::<SocketAddr>().unwrap();
186  /// let host = HostAddr::<String>::from(addr);
187  /// # }
188  /// ```
189  fn from(addr: SocketAddr) -> Self {
190    Self::from_sock_addr(addr)
191  }
192}
193
194impl<S> From<SocketAddrV4> for HostAddr<S> {
195  /// ```rust
196  /// # #[cfg(any(feature = "std", feature = "alloc"))]
197  /// # {
198  /// use hostaddr::HostAddr;
199  /// use std::net::SocketAddrV4;
200  ///
201  /// let addr = "127.0.0.1:8080".parse::<SocketAddrV4>().unwrap();
202  /// let host = HostAddr::<String>::from(addr);
203  /// # }
204  /// ```
205  fn from(addr: SocketAddrV4) -> Self {
206    Self::from_sock_addr(SocketAddr::V4(addr))
207  }
208}
209
210impl<S> From<SocketAddrV6> for HostAddr<S> {
211  /// ```rust
212  /// # #[cfg(any(feature = "std", feature = "alloc"))]
213  /// # {
214  /// use hostaddr::HostAddr;
215  /// use std::net::SocketAddrV6;
216  ///
217  /// let addr = "[::1]:8080".parse::<SocketAddrV6>().unwrap();
218  /// let host = HostAddr::<String>::from(addr);
219  /// # }
220  /// ```
221  fn from(addr: SocketAddrV6) -> Self {
222    Self::from_sock_addr(SocketAddr::V6(addr))
223  }
224}
225
226impl<S> From<IpAddr> for HostAddr<S> {
227  /// ```rust
228  /// # #[cfg(any(feature = "std", feature = "alloc"))]
229  /// # {
230  /// use hostaddr::HostAddr;
231  /// use std::net::IpAddr;
232  ///
233  /// let ip = "127.0.0.1".parse::<IpAddr>().unwrap();
234  /// let addr = HostAddr::<String>::from(ip);
235  /// # }
236  /// ```
237  fn from(ip: IpAddr) -> Self {
238    Self::from_ip_addr(ip)
239  }
240}
241
242impl<S> From<Ipv4Addr> for HostAddr<S> {
243  /// ```rust
244  /// # #[cfg(any(feature = "std", feature = "alloc"))]
245  /// # {
246  /// use hostaddr::HostAddr;
247  /// use std::net::Ipv4Addr;
248  ///
249  /// let ip = "127.0.0.1".parse::<Ipv4Addr>().unwrap();
250  /// let addr = HostAddr::<String>::from(ip);
251  /// # }
252  /// ```
253  fn from(ip: Ipv4Addr) -> Self {
254    Self::from(IpAddr::V4(ip))
255  }
256}
257
258impl<S> From<Ipv6Addr> for HostAddr<S> {
259  /// ```rust
260  /// # #[cfg(any(feature = "std", feature = "alloc"))]
261  /// # {
262  /// use hostaddr::HostAddr;
263  /// use std::net::Ipv6Addr;
264  ///
265  /// let ip = "::1".parse::<Ipv6Addr>().unwrap();
266  /// let addr = HostAddr::<String>::from(ip);
267  /// # }
268  /// ```
269  fn from(ip: Ipv6Addr) -> Self {
270    Self::from(IpAddr::V6(ip))
271  }
272}
273
274impl<S> From<HostAddr<S>> for HostAddr<Domain<S>> {
275  /// ```rust
276  /// # #[cfg(any(feature = "std", feature = "alloc"))]
277  /// # {
278  /// use hostaddr::{HostAddr, Domain};
279  /// use std::net::Ipv6Addr;
280  ///
281  /// let ip = "::1".parse::<Ipv6Addr>().unwrap();
282  /// let addr = HostAddr::<String>::from(ip);
283  /// let domain_addr: HostAddr<Domain<String>> = addr.into();
284  /// # }
285  /// ```
286  #[inline]
287  fn from(value: HostAddr<S>) -> Self {
288    let (host, port) = value.into_components();
289    match host {
290      Host::Domain(domain) => Self {
291        // Safety: the ways to construct a valid HostAddr guarantee the domain must be valid.
292        host: Host::Domain(unsafe { Domain::new_unchecked(domain) }),
293        port,
294      },
295      Host::Ip(ip) => Self {
296        host: Host::Ip(ip),
297        port,
298      },
299    }
300  }
301}
302
303impl<'a, S> From<&'a HostAddr<S>> for HostAddr<&'a Domain<S>> {
304  /// ```rust
305  /// # #[cfg(any(feature = "std", feature = "alloc"))]
306  /// # {
307  /// use hostaddr::{HostAddr, Domain};
308  /// use std::net::Ipv6Addr;
309  ///
310  /// let ip = "::1".parse::<Ipv6Addr>().unwrap();
311  /// let addr = HostAddr::<String>::from(ip);
312  ///
313  /// let domain_addr: HostAddr<&Domain<String>> = (&addr).into();
314  /// # }
315  /// ```
316  #[inline]
317  fn from(value: &'a HostAddr<S>) -> Self {
318    let (host, port) = value.as_ref().into_components();
319    match host {
320      Host::Domain(domain) => Self {
321        // Safety: the ways to construct a valid HostAddr guarantee the domain must be valid.
322        host: Host::Domain(unsafe { Domain::from_ref_unchecked(domain) }),
323        port,
324      },
325      Host::Ip(ip) => Self {
326        host: Host::Ip(ip),
327        port,
328      },
329    }
330  }
331}
332
333impl<S> HostAddr<S> {
334  /// Create a new host address
335  ///
336  /// ## Example
337  ///
338  /// ```rust
339  /// # #[cfg(any(feature = "std", feature = "alloc"))]
340  /// # {
341  /// use hostaddr::HostAddr;
342  ///
343  /// let host = HostAddr::<String>::new("example.com".parse().unwrap());
344  /// println!("{}", host);
345  /// # }
346  /// ```
347  #[inline]
348  pub const fn new(host: Host<S>) -> Self {
349    Self { host, port: None }
350  }
351
352  /// Create a new host address from a domain name
353  ///
354  /// ## Example
355  ///
356  /// ```rust
357  /// # #[cfg(any(feature = "std", feature = "alloc"))]
358  /// # {
359  /// use hostaddr::{HostAddr, Domain};
360  ///
361  /// let host = HostAddr::<String>::from_domain("example.com".parse().unwrap());
362  /// println!("{}", host);
363  /// # }
364  /// ```
365  #[inline]
366  pub fn from_domain(domain: Domain<S>) -> Self {
367    Self {
368      host: Host::Domain(domain.0),
369      port: None,
370    }
371  }
372
373  /// Create a new host address from an IP address
374  ///
375  /// ## Example
376  ///
377  /// ```rust
378  /// # #[cfg(any(feature = "std", feature = "alloc"))]
379  /// # {
380  /// use hostaddr::HostAddr;
381  ///
382  /// let host = HostAddr::<String>::from_ip_addr("127.0.0.1".parse().unwrap());
383  /// println!("{}", host);
384  /// # }
385  /// ```
386  #[inline]
387  pub const fn from_ip_addr(ip: IpAddr) -> Self {
388    Self {
389      host: Host::Ip(ip),
390      port: None,
391    }
392  }
393
394  /// Create a new host address from a `SocketAddr`
395  ///
396  /// ## Example
397  ///
398  /// ```rust
399  /// # #[cfg(any(feature = "std", feature = "alloc"))]
400  /// # {
401  /// use hostaddr::HostAddr;
402  ///
403  /// let host = HostAddr::<String>::from_sock_addr("127.0.0.1:8080".parse().unwrap());
404  /// println!("{}", host);
405  /// # }
406  /// ```
407  #[inline]
408  pub const fn from_sock_addr(addr: SocketAddr) -> Self {
409    Self {
410      host: Host::Ip(addr.ip()),
411      port: Some(addr.port()),
412    }
413  }
414
415  /// Get the host name
416  ///
417  /// ## Example
418  ///
419  /// ```rust
420  /// # #[cfg(any(feature = "std", feature = "alloc"))]
421  /// # {
422  /// use hostaddr::HostAddr;
423  ///
424  /// let addr: HostAddr<String> = "example.com:8080".parse().unwrap();
425  /// println!("{}", addr.host());
426  /// # }
427  /// ```
428  #[inline]
429  pub const fn host(&self) -> &Host<S> {
430    &self.host
431  }
432
433  /// Get the ip address
434  ///
435  /// ## Example
436  ///
437  /// ```rust
438  /// # #[cfg(any(feature = "std", feature = "alloc"))]
439  /// # {
440  /// use hostaddr::HostAddr;
441  ///
442  /// let addr: HostAddr<String> = HostAddr::from_ip_addr("127.0.0.1".parse().unwrap());
443  /// println!("{}", addr.ip().unwrap());
444  /// # }
445  /// ```
446  #[inline]
447  pub const fn ip(&self) -> Option<&IpAddr> {
448    self.host.ip()
449  }
450
451  /// Get the port number
452  ///
453  /// ## Example
454  ///
455  /// ```rust
456  /// # #[cfg(any(feature = "std", feature = "alloc"))]
457  /// # {
458  /// use hostaddr::HostAddr;
459  ///
460  /// let addr: HostAddr<String> = "example.com:8080".parse().unwrap();
461  ///
462  /// assert_eq!(Some(8080), addr.port());
463  /// # }
464  /// ```
465  #[inline]
466  pub const fn port(&self) -> Option<u16> {
467    self.port
468  }
469
470  /// Set the port number
471  ///
472  /// ## Example
473  ///
474  /// ```rust
475  /// # #[cfg(any(feature = "std", feature = "alloc"))]
476  /// # {
477  /// use hostaddr::HostAddr;
478  ///
479  /// let mut host: HostAddr<String> = "example.com".parse().unwrap();
480  /// host
481  ///   .set_port(8080)
482  ///   .set_host("example.org".parse().unwrap());
483  /// assert_eq!(Some(8080), host.port());
484  /// # }
485  /// ```
486  #[inline]
487  pub const fn set_port(&mut self, port: u16) -> &mut Self {
488    self.port = Some(port);
489    self
490  }
491
492  /// Set the port number
493  ///
494  /// See also [`maybe_with_port`](Self::maybe_with_port).
495  ///
496  /// ## Example
497  ///
498  /// ```rust
499  /// # #[cfg(any(feature = "std", feature = "alloc"))]
500  /// # {
501  /// use hostaddr::HostAddr;
502  ///
503  /// let mut host: HostAddr<String> = "example.com".parse().unwrap();
504  /// host
505  ///   .maybe_port(Some(8080))
506  ///   .set_host("example.org".parse().unwrap());
507  /// assert_eq!(Some(8080), host.port());
508  /// # }
509  /// ```
510  #[inline]
511  pub const fn maybe_port(&mut self, port: Option<u16>) -> &mut Self {
512    self.port = port;
513    self
514  }
515
516  /// Set the port number
517  ///
518  /// See also [`maybe_port`](Self::maybe_port).
519  ///
520  /// ## Example
521  ///
522  /// ```rust
523  /// # #[cfg(any(feature = "std", feature = "alloc"))]
524  /// # {
525  /// use hostaddr::HostAddr;
526  ///
527  /// let host  = "example.com".parse::<HostAddr<String>>().unwrap().maybe_with_port(Some(8080));
528  /// assert_eq!(Some(8080), host.port());
529  /// # }
530  /// ```
531  #[inline]
532  pub const fn maybe_with_port(mut self, port: Option<u16>) -> Self {
533    self.port = port;
534    self
535  }
536
537  /// Set the port number
538  ///
539  /// See also [`set_port`](Self::set_port).
540  ///
541  /// ## Example
542  ///
543  /// ```rust
544  /// # #[cfg(any(feature = "std", feature = "alloc"))]
545  /// # {
546  /// use hostaddr::HostAddr;
547  ///
548  /// let host = "example.com".parse::<HostAddr<String>>().unwrap().with_port(8080);
549  /// assert_eq!(Some(8080), host.port());
550  /// # }
551  /// ```
552  #[inline]
553  pub const fn with_port(mut self, port: u16) -> Self {
554    self.port = Some(port);
555    self
556  }
557
558  /// Set a default port if no port is currently set.
559  ///
560  /// If a port is already set, this method does nothing.
561  ///
562  /// ## Example
563  ///
564  /// ```rust
565  /// # #[cfg(any(feature = "std", feature = "alloc"))]
566  /// # {
567  /// use hostaddr::HostAddr;
568  ///
569  /// let addr = "example.com".parse::<HostAddr<String>>().unwrap()
570  ///   .with_default_port(443);
571  /// assert_eq!(Some(443), addr.port());
572  ///
573  /// let addr = "example.com:8080".parse::<HostAddr<String>>().unwrap()
574  ///   .with_default_port(443);
575  /// assert_eq!(Some(8080), addr.port());
576  /// # }
577  /// ```
578  #[inline]
579  pub const fn with_default_port(mut self, default: u16) -> Self {
580    if self.port.is_none() {
581      self.port = Some(default);
582    }
583    self
584  }
585
586  /// Clear the port number.
587  ///
588  /// ## Example
589  ///
590  /// ```rust
591  /// # #[cfg(any(feature = "std", feature = "alloc"))]
592  /// # {
593  /// use hostaddr::HostAddr;
594  ///
595  /// let mut addr: HostAddr<String> = "example.com:8080".parse().unwrap();
596  /// addr.clear_port();
597  /// assert_eq!(None, addr.port());
598  /// # }
599  /// ```
600  #[inline]
601  pub const fn clear_port(&mut self) -> &mut Self {
602    self.port = None;
603    self
604  }
605
606  /// Returns `true` if a port is set.
607  ///
608  /// ## Example
609  ///
610  /// ```rust
611  /// # #[cfg(any(feature = "std", feature = "alloc"))]
612  /// # {
613  /// use hostaddr::HostAddr;
614  ///
615  /// let addr: HostAddr<String> = "example.com:8080".parse().unwrap();
616  /// assert!(addr.has_port());
617  ///
618  /// let addr: HostAddr<String> = "example.com".parse().unwrap();
619  /// assert!(!addr.has_port());
620  /// # }
621  /// ```
622  #[inline]
623  pub const fn has_port(&self) -> bool {
624    self.port.is_some()
625  }
626
627  /// Set the host name
628  ///
629  /// ## Example
630  ///
631  /// ```rust
632  /// # #[cfg(any(feature = "std", feature = "alloc"))]
633  /// # {
634  /// use hostaddr::HostAddr;
635  ///
636  /// let mut addr: HostAddr<String> = "example.com".parse().unwrap();
637  /// addr
638  ///   .set_host("example.org".parse().unwrap())
639  ///   .set_port(8080);
640  /// assert_eq!("example.org", addr.as_ref().host().unwrap_domain().as_str());
641  /// # }
642  /// ```
643  #[inline]
644  pub fn set_host(&mut self, host: Host<S>) -> &mut Self {
645    self.host = host;
646    self
647  }
648
649  /// Set the host name
650  ///
651  /// ## Example
652  ///
653  /// ```rust
654  /// # #[cfg(any(feature = "std", feature = "alloc"))]
655  /// # {
656  /// use hostaddr::HostAddr;
657  ///
658  /// let addr: HostAddr<String> = HostAddr::from_sock_addr("127.0.0.1:8080".parse().unwrap())
659  ///   .with_host("example.com".parse().unwrap());
660  /// assert_eq!("example.com", addr.as_ref().host().unwrap_domain().as_str());
661  /// # }
662  /// ```
663  #[inline]
664  pub fn with_host(mut self, host: Host<S>) -> Self {
665    self.host = host;
666    self
667  }
668
669  /// Returns `true` if the host is an IP address
670  ///
671  /// ## Example
672  ///
673  /// ```rust
674  /// # #[cfg(any(feature = "std", feature = "alloc"))]
675  /// # {
676  /// use hostaddr::HostAddr;
677  ///
678  /// let host: HostAddr<String> = HostAddr::from_sock_addr("127.0.0.1:8080".parse().unwrap());
679  /// assert!(host.is_ip());
680  /// # }
681  #[inline]
682  pub const fn is_ip(&self) -> bool {
683    self.host.is_ip()
684  }
685
686  /// Returns `true` if the host is an Ipv4 address
687  ///
688  /// ## Example
689  ///
690  /// ```rust
691  /// # #[cfg(any(feature = "std", feature = "alloc"))]
692  /// # {
693  /// use hostaddr::HostAddr;
694  ///
695  /// let host: HostAddr<String> = HostAddr::from_sock_addr("127.0.0.1:8080".parse().unwrap());
696  /// assert!(host.is_ipv4());
697  /// # }
698  /// ```
699  #[inline]
700  pub const fn is_ipv4(&self) -> bool {
701    self.host.is_ipv4()
702  }
703
704  /// Returns `true` if the host is an Ipv6 address
705  ///
706  /// ## Example
707  ///
708  /// ```rust
709  /// # #[cfg(any(feature = "std", feature = "alloc"))]
710  /// # {
711  /// use hostaddr::HostAddr;
712  ///
713  /// let host: HostAddr<String> = HostAddr::from_sock_addr("[::1]:8080".parse().unwrap());
714  /// assert!(host.is_ipv6());
715  /// # }
716  /// ```
717  #[inline]
718  pub const fn is_ipv6(&self) -> bool {
719    self.host.is_ipv6()
720  }
721
722  /// Returns `true` if the host is a domain name
723  ///
724  /// ## Example
725  ///
726  /// ```rust
727  /// # #[cfg(any(feature = "std", feature = "alloc"))]
728  /// # {
729  /// use hostaddr::HostAddr;
730  ///
731  /// let host: HostAddr<String> = "example.com".parse().unwrap();
732  /// assert!(host.is_domain());
733  /// # }
734  /// ```
735  #[inline]
736  pub const fn is_domain(&self) -> bool {
737    self.host.is_domain()
738  }
739
740  /// Returns `true` if the host represents localhost.
741  ///
742  /// This method checks if the host is:
743  /// - An IPv4 loopback address (127.0.0.0/8)
744  /// - An IPv6 loopback address (::1)
745  /// - The domain name "localhost"
746  ///
747  /// ## Example
748  ///
749  /// ```rust
750  /// use hostaddr::HostAddr;
751  ///
752  /// let addr = HostAddr::try_from_ascii_str("127.0.0.1").unwrap();
753  /// assert!(addr.is_localhost());
754  ///
755  /// let addr = HostAddr::try_from_ascii_str("::1").unwrap();
756  /// assert!(addr.is_localhost());
757  ///
758  /// let addr = HostAddr::try_from_ascii_str("localhost").unwrap();
759  /// assert!(addr.is_localhost());
760  ///
761  /// let addr = HostAddr::try_from_ascii_str("example.com").unwrap();
762  /// assert!(!addr.is_localhost());
763  /// ```
764  #[inline]
765  pub fn is_localhost(&self) -> bool
766  where
767    S: AsRef<str>,
768  {
769    match &self.host {
770      Host::Ip(IpAddr::V4(ip)) => ip.is_loopback(),
771      Host::Ip(IpAddr::V6(ip)) => ip.is_loopback(),
772      Host::Domain(domain) => {
773        let s = domain.as_ref();
774        s.eq_ignore_ascii_case("localhost") || s.eq_ignore_ascii_case("localhost.")
775      }
776    }
777  }
778
779  /// Converts to a `SocketAddr` if the host is an IP address and a port is set.
780  ///
781  /// Returns `None` if the host is a domain name or if no port is set.
782  ///
783  /// ## Example
784  ///
785  /// ```rust
786  /// # #[cfg(any(feature = "std", feature = "alloc"))]
787  /// # {
788  /// use hostaddr::HostAddr;
789  /// use std::net::{IpAddr, SocketAddr};
790  ///
791  /// let addr: HostAddr<String> = "127.0.0.1:8080".parse().unwrap();
792  /// assert_eq!(
793  ///   Some(SocketAddr::new("127.0.0.1".parse::<IpAddr>().unwrap(), 8080)),
794  ///   addr.to_socket_addr()
795  /// );
796  ///
797  /// // Domain names cannot be converted to SocketAddr
798  /// let addr: HostAddr<String> = "example.com:8080".parse().unwrap();
799  /// assert_eq!(None, addr.to_socket_addr());
800  ///
801  /// // Missing port returns None
802  /// let addr: HostAddr<String> = "127.0.0.1".parse().unwrap();
803  /// assert_eq!(None, addr.to_socket_addr());
804  /// # }
805  /// ```
806  #[inline]
807  pub const fn to_socket_addr(&self) -> Option<SocketAddr> {
808    match (&self.host, self.port) {
809      (Host::Ip(ip), Some(port)) => Some(SocketAddr::new(*ip, port)),
810      _ => None,
811    }
812  }
813
814  /// Converts from `&HostAddr<S>` to `HostAddr<&S>`.
815  ///
816  ///
817  /// ## Example
818  ///
819  /// ```rust
820  /// # #[cfg(any(feature = "std", feature = "alloc"))]
821  /// # {
822  /// use std::sync::Arc;
823  /// use hostaddr::HostAddr;
824  ///
825  /// let host: HostAddr<Arc<str>> = "example.com:8080".try_into().unwrap();
826  /// assert_eq!("example.com", &**host.as_ref().host().unwrap_domain());
827  /// # }
828  /// ```
829  #[inline]
830  pub const fn as_ref(&self) -> HostAddr<&S> {
831    HostAddr {
832      host: self.host.as_ref(),
833      port: self.port,
834    }
835  }
836
837  /// Converts from `HostAddr<S>` (or `&HostAddr<S>`) to `HostAddr<&S::Target>`.
838  ///
839  /// ## Example
840  ///
841  /// ```rust
842  /// # #[cfg(any(feature = "std", feature = "alloc"))]
843  /// # {
844  /// use std::sync::Arc;
845  /// use hostaddr::HostAddr;
846  ///
847  /// let host = "example.com:9090".parse::<HostAddr<Arc<str>>>().unwrap();
848  /// assert_eq!("example.com", host.as_deref().host().unwrap_domain());
849  /// # }
850  /// ```
851  #[inline]
852  pub fn as_deref(&self) -> HostAddr<&S::Target>
853  where
854    S: core::ops::Deref,
855  {
856    HostAddr {
857      host: self.host.as_deref(),
858      port: self.port,
859    }
860  }
861
862  /// Consumes the `HostAddr` and returns the host name and port number.
863  ///
864  /// ## Example
865  ///
866  /// ```rust
867  /// # #[cfg(any(feature = "std", feature = "alloc"))]
868  /// # {
869  /// use hostaddr::HostAddr;
870  ///
871  /// let host: HostAddr<String> = "example.com:8080".parse().unwrap();
872  /// let (host, port) = host.into_components();
873  /// assert_eq!("example.com", host.unwrap_domain().as_str());
874  /// assert_eq!(Some(8080), port);
875  /// # }
876  /// ```
877  #[inline]
878  pub fn into_components(self) -> (Host<S>, Option<u16>) {
879    let Self { host, port } = self;
880
881    (host, port)
882  }
883
884  /// Unwraps the domain, panics if the host is an IP address.
885  ///
886  /// ## Example
887  ///
888  /// ```rust
889  /// # #[cfg(any(feature = "std", feature = "alloc"))]
890  /// # {
891  /// use hostaddr::HostAddr;
892  ///
893  /// let host: HostAddr<String> = "example.com".parse().unwrap();
894  /// let (domain, port) = host.unwrap_domain();
895  /// assert_eq!("example.com", domain.as_str());
896  /// assert_eq!(None, port);
897  /// # }
898  /// ```
899  #[inline]
900  pub fn unwrap_domain(self) -> (S, Option<u16>) {
901    (self.host.unwrap_domain(), self.port)
902  }
903
904  /// Unwraps the IP address, panics if the host is a domain name.
905  ///
906  /// ## Example
907  ///
908  /// ```rust
909  /// # #[cfg(any(feature = "std", feature = "alloc"))]
910  /// # {
911  /// use hostaddr::HostAddr;
912  ///
913  /// let host: HostAddr<String> = HostAddr::from_sock_addr("[::1]:8080".parse().unwrap());
914  /// let (ip, port) = host.unwrap_ip();
915  /// assert_eq!(ip, "::1".parse::<std::net::IpAddr>().unwrap());
916  /// assert_eq!(Some(8080), port);
917  /// # }
918  /// ```
919  #[inline]
920  pub fn unwrap_ip(self) -> (IpAddr, Option<u16>) {
921    (self.host.unwrap_ip(), self.port)
922  }
923}
924
925impl<S> HostAddr<&S> {
926  /// Maps an `HostAddr<&S>` to an `HostAddr<S>` by copying the contents of the
927  /// host addr.
928  ///
929  /// ## Example
930  ///
931  /// ```rust
932  /// # #[cfg(any(feature = "std", feature = "alloc"))]
933  /// # {
934  /// use hostaddr::{HostAddr, Buffer};
935  ///
936  /// let host: HostAddr<Buffer> = HostAddr::try_from("example.com").unwrap();
937  /// assert_eq!("example.com", host.as_ref().copied().unwrap_domain().0.as_str());
938  /// # }
939  /// ```
940  #[inline]
941  pub const fn copied(self) -> HostAddr<S>
942  where
943    S: Copy,
944  {
945    HostAddr {
946      host: self.host.copied(),
947      port: self.port,
948    }
949  }
950
951  /// Maps an `HostAddr<&S>` to an `HostAddr<S>` by cloning the contents of the
952  /// host addr.
953  ///
954  /// ## Example
955  ///
956  /// ```rust
957  /// # #[cfg(any(feature = "std", feature = "alloc"))]
958  /// # {
959  /// use hostaddr::HostAddr;
960  ///
961  /// let host: HostAddr<String> = "example.com".parse().unwrap();
962  /// assert_eq!("example.com", host.as_ref().cloned().unwrap_domain().0.as_str());
963  /// # }
964  /// ```
965  #[inline]
966  pub fn cloned(self) -> HostAddr<S>
967  where
968    S: Clone,
969  {
970    HostAddr {
971      host: self.host.cloned(),
972      port: self.port,
973    }
974  }
975}
976
977macro_rules! try_from_str {
978  ($convert:ident($s: ident)) => {{
979    match try_parse_v6::<S>($s)? {
980      Some(addr) => Ok(addr),
981      None => {
982        let mut parts = $s.splitn(2, ':');
983        let host = parts.next().ok_or(ParseHostAddrError::host())?.$convert()?;
984        let port = match parts.next() {
985          Some(port) => Some(port.parse().map_err(ParseHostAddrError::Port)?),
986          None => None,
987        };
988
989        Ok(Self { host, port })
990      }
991    }
992  }};
993}
994
995impl<'a> HostAddr<&'a str> {
996  /// Parses a [`HostAddr`] name from `&str`.
997  ///
998  /// Unlike `HostAddr::try_from` or `HostAddr::from_str`, this method does not perform any percent decoding
999  /// or punycode decoding. If the input is not ASCII, it will return an error.
1000  ///
1001  /// ## Example
1002  ///
1003  /// ```rust
1004  /// use hostaddr::HostAddr;
1005  ///
1006  /// let host = HostAddr::try_from_ascii_str("example.com").unwrap();
1007  /// assert_eq!(host.unwrap_domain().0, "example.com");
1008  ///
1009  /// // This will return an error because the domain is not ASCII.
1010  /// assert!(HostAddr::try_from_ascii_str("测试.中国").is_err());
1011  ///
1012  /// // Thie will not return an error, even though the human-readable domain is not ASCII.
1013  /// let host = HostAddr::try_from_ascii_str("xn--0zwm56d.xn--fiqs8s").unwrap();
1014  /// assert_eq!(host.unwrap_domain().0, "xn--0zwm56d.xn--fiqs8s");
1015  /// ```
1016  #[inline]
1017  pub fn try_from_ascii_str(input: &'a str) -> Result<Self, ParseAsciiHostAddrError> {
1018    match try_parse_v6(input).map_err(|_| ParseAsciiHostAddrError::host())? {
1019      Some(addr) => Ok(addr),
1020      None => {
1021        if let Ok(ip) = input.parse() {
1022          return Ok(Self::from_ip_addr(ip));
1023        }
1024
1025        let mut parts = input.splitn(2, ':');
1026        let host = Host::try_from_ascii_str(parts.next().ok_or(ParseAsciiHostAddrError::host())?)?;
1027        let port = match parts.next() {
1028          Some(port) => Some(port.parse().map_err(ParseAsciiHostAddrError::Port)?),
1029          None => None,
1030        };
1031
1032        Ok(Self { host, port })
1033      }
1034    }
1035  }
1036
1037  /// Converts the domain to a `HostAddr<&'a [u8]>`.
1038  ///
1039  /// ## Example
1040  ///
1041  /// ```rust
1042  /// use hostaddr::HostAddr;
1043  ///
1044  /// let addr = HostAddr::try_from_ascii_str("example.com").unwrap();
1045  /// assert_eq!(addr.as_bytes().unwrap_domain().0, b"example.com");
1046  /// ```
1047  #[inline]
1048  pub const fn as_bytes(&self) -> HostAddr<&'a [u8]> {
1049    HostAddr {
1050      host: self.host.as_bytes(),
1051      port: self.port,
1052    }
1053  }
1054}
1055
1056impl<'a> HostAddr<&'a [u8]> {
1057  /// Parses a [`HostAddr`] from `&[u8]`.
1058  ///
1059  /// Unlike `HostAddr::try_from` or `HostAddr::from_str`, this method does not perform any percent decoding
1060  /// or punycode decoding. If the input is not ASCII, it will return an error.
1061  ///
1062  /// ## Example
1063  ///
1064  /// ```rust
1065  /// use hostaddr::HostAddr;
1066  ///
1067  /// let host = HostAddr::try_from_ascii_bytes(b"example.com").unwrap();
1068  /// assert_eq!(host.unwrap_domain().0, b"example.com");
1069  ///
1070  /// // This will return an error because the domain is not ASCII.
1071  /// assert!(HostAddr::try_from_ascii_bytes("测试.中国".as_bytes()).is_err());
1072  ///
1073  /// // Thie will not return an error, even though the human-readable domain is not ASCII.
1074  /// let host = HostAddr::try_from_ascii_bytes(b"xn--0zwm56d.xn--fiqs8s").unwrap();
1075  /// assert_eq!(host.unwrap_domain().0, b"xn--0zwm56d.xn--fiqs8s");
1076  /// ```
1077  #[inline]
1078  pub fn try_from_ascii_bytes(input: &'a [u8]) -> Result<Self, ParseAsciiHostAddrError> {
1079    let input_str = simdutf8::basic::from_utf8(input).map_err(|_| ParseAsciiHostError(()))?;
1080    match try_parse_v6(input_str).map_err(|_| ParseAsciiHostAddrError::host())? {
1081      Some(addr) => Ok(addr),
1082      None => {
1083        // if it's an pure ip address?
1084        if let Ok(ip) = IpAddr::from_str(input_str) {
1085          return Ok(Self::from_ip_addr(ip));
1086        }
1087
1088        let mut parts = input_str.splitn(2, ':');
1089        let host = Host::try_from_ascii_bytes(
1090          parts
1091            .next()
1092            .map(|s| s.as_bytes())
1093            .ok_or(ParseAsciiHostAddrError::host())?,
1094        )?;
1095        let port = match parts.next() {
1096          Some(port) => Some(port.parse().map_err(ParseAsciiHostAddrError::Port)?),
1097          None => None,
1098        };
1099
1100        Ok(Self { host, port })
1101      }
1102    }
1103  }
1104
1105  /// Converts the domain to a `HostAddr<&'a str>`.
1106  ///
1107  /// ## Example
1108  ///
1109  /// ```rust
1110  /// use hostaddr::HostAddr;
1111  ///
1112  /// let addr = HostAddr::try_from_ascii_bytes(b"example.com").unwrap();
1113  /// assert_eq!(addr.as_str().unwrap_domain().0, "example.com");
1114  /// ```
1115  #[inline]
1116  pub const fn as_str(&self) -> HostAddr<&'a str> {
1117    HostAddr {
1118      host: self.host.as_str(),
1119      port: self.port,
1120    }
1121  }
1122}
1123
1124impl<S> FromStr for HostAddr<S>
1125where
1126  Domain<S>: FromStr,
1127{
1128  type Err = ParseHostAddrError;
1129
1130  fn from_str(s: &str) -> Result<Self, Self::Err> {
1131    try_from_str!(parse(s))
1132  }
1133}
1134
1135impl<'a, S> TryFrom<&'a str> for HostAddr<S>
1136where
1137  Domain<S>: TryFrom<&'a str>,
1138{
1139  type Error = ParseHostAddrError;
1140
1141  fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1142    try_from_str!(try_into(s))
1143  }
1144}
1145
1146impl<'a, S> TryFrom<(&'a str, u16)> for HostAddr<S>
1147where
1148  Domain<S>: TryFrom<&'a str>,
1149{
1150  type Error = ParseHostAddrError;
1151
1152  fn try_from((s, port): (&'a str, u16)) -> Result<Self, Self::Error> {
1153    let host = Host::try_from(s)?;
1154    Ok(Self {
1155      host,
1156      port: Some(port),
1157    })
1158  }
1159}
1160
1161fn try_parse_v6<S>(s: &str) -> Result<Option<HostAddr<S>>, ParseHostAddrError> {
1162  if let Some(without_prefix) = s.strip_prefix('[') {
1163    // Ipv6 and no port
1164    if let Some(ip) = without_prefix.strip_suffix(']') {
1165      return ip
1166        .parse()
1167        .map_err(|_| ParseHostAddrError::host())
1168        .map(|addr| Some(HostAddr::from_ip_addr(addr)));
1169    }
1170
1171    // Ipv6 with port
1172    let mut parts = s.rsplitn(2, ':');
1173
1174    let port = parts.next();
1175    let host = parts.next();
1176
1177    match (host, port) {
1178      (Some(host), Some(_)) if host.ends_with("]") => {
1179        return s
1180          .parse()
1181          .map_err(|_| ParseHostAddrError::host())
1182          .map(|addr| Some(HostAddr::from_sock_addr(addr)));
1183      }
1184      _ => return Err(ParseHostAddrError::host()),
1185    }
1186  }
1187
1188  Ok(None)
1189}
1190
1191#[cfg(test)]
1192mod tests {
1193  use super::*;
1194
1195  #[test]
1196  fn test_hostaddr_parsing() {
1197    #[cfg(any(feature = "std", feature = "alloc"))]
1198    {
1199      use std::string::String;
1200
1201      let host: HostAddr<String> = "example.com".parse().unwrap();
1202      assert_eq!("example.com", host.as_ref().host().unwrap_domain());
1203
1204      let host: HostAddr<String> = "example.com:8080".parse().unwrap();
1205      assert_eq!("example.com", host.as_ref().host().unwrap_domain());
1206      assert_eq!(Some(8080), host.port());
1207
1208      let host: HostAddr<String> = "127.0.0.1:8080".parse().unwrap();
1209      assert_eq!(
1210        IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
1211        host.as_ref().host().unwrap_ip()
1212      );
1213      assert_eq!(Some(8080), host.port());
1214
1215      let host: HostAddr<String> = "[::1]:8080".parse().unwrap();
1216      assert_eq!(
1217        IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
1218        host.as_ref().host().unwrap_ip()
1219      );
1220      assert_eq!(Some(8080), host.port());
1221    }
1222
1223    let host: HostAddr<&str> = HostAddr::try_from_ascii_str("[::1]").unwrap();
1224    assert_eq!(
1225      IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
1226      host.as_ref().host().unwrap_ip()
1227    );
1228
1229    let host: HostAddr<&[u8]> = HostAddr::try_from_ascii_bytes(b"[::1]").unwrap();
1230    assert_eq!(
1231      IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
1232      host.as_ref().host().unwrap_ip()
1233    );
1234
1235    let host: HostAddr<&[u8]> = HostAddr::try_from_ascii_bytes(b"::1").unwrap();
1236    assert_eq!(
1237      IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
1238      host.as_ref().host().unwrap_ip()
1239    );
1240
1241    let host: HostAddr<&str> = HostAddr::try_from_ascii_str("::1").unwrap();
1242    assert_eq!(
1243      IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
1244      host.as_ref().host().unwrap_ip()
1245    );
1246  }
1247
1248  #[cfg(any(feature = "std", feature = "alloc"))]
1249  #[test]
1250  fn test_hostaddr_try_into() {
1251    use std::string::String;
1252
1253    let host: HostAddr<String> = "example.com".try_into().unwrap();
1254    assert_eq!("example.com", host.as_ref().host().unwrap_domain());
1255
1256    let host: HostAddr<String> = "example.com:8080".try_into().unwrap();
1257    assert_eq!("example.com", host.as_ref().host().unwrap_domain());
1258    assert_eq!(Some(8080), host.port());
1259
1260    let host: HostAddr<String> = "127.0.0.1:8080".try_into().unwrap();
1261    assert_eq!(
1262      IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
1263      host.as_ref().host().unwrap_ip()
1264    );
1265    assert_eq!(Some(8080), host.port());
1266
1267    let host: HostAddr<String> = "[::1]:8080".try_into().unwrap();
1268    assert_eq!(
1269      IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
1270      host.as_ref().host().unwrap_ip()
1271    );
1272    assert_eq!(Some(8080), host.port());
1273  }
1274
1275  #[test]
1276  fn negative_try_parse_v6() {
1277    let _ = try_parse_v6::<&str>("[a]").unwrap_err();
1278    let _ = try_parse_v6::<&str>("[a]:8080").unwrap_err();
1279    let _ = try_parse_v6::<&str>("[a:8080").unwrap_err();
1280  }
1281
1282  #[test]
1283  fn negative_try_from_ascii_bytes() {
1284    let err = HostAddr::try_from_ascii_bytes(b"example.com:aaa").unwrap_err();
1285    assert!(matches!(err, ParseAsciiHostAddrError::Port(_)));
1286  }
1287
1288  #[test]
1289  fn negative_try_from_ascii_str() {
1290    let err = HostAddr::try_from_ascii_str("example.com:aaa").unwrap_err();
1291    assert!(matches!(err, ParseAsciiHostAddrError::Port(_)));
1292  }
1293}