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}