ip/any/
addr.rs

1use core::cmp::Ordering;
2use core::fmt;
3use core::str::FromStr;
4
5use super::delegate;
6use crate::{
7    concrete::{self, Ipv4, Ipv6},
8    error::Error,
9    traits::{
10        self,
11        primitive::{Address as _, IntoIpv6Segments as _},
12        Afi,
13    },
14};
15
16/// Either an IPv4 or IPv6 address.
17///
18/// # Memory Use
19///
20/// Rust enums are sized to accommodate their largest variant, with smaller
21/// variants being padded to fill up any unused space.
22///
23/// As a result, users should avoid using this type in a context where only
24/// [`Address::Ipv4`] variants are expected.
25///
26/// # Examples
27///
28/// ``` rust
29/// use ip::{traits::Address as _, Address, Any};
30///
31/// let addr = "2001:db8::1".parse::<Address<Any>>()?;
32///
33/// assert!(addr.is_documentation());
34/// # Ok::<(), ip::Error>(())
35/// ```
36#[allow(variant_size_differences)]
37#[derive(Clone, Copy, Hash, PartialEq, Eq)]
38pub enum Address {
39    /// IPv4 address variant.
40    Ipv4(concrete::Address<Ipv4>),
41    /// IPv6 address variant.
42    Ipv6(concrete::Address<Ipv6>),
43}
44
45impl Address {
46    /// Returns [`true`] if this is an IPv4 address.
47    ///
48    /// # Examples
49    ///
50    /// ``` rust
51    /// use ip::{traits::Address as _, Address, Any};
52    ///
53    /// let ipv4_addr = "192.0.2.1".parse::<Address<Any>>()?;
54    /// let ipv6_addr = "2001:db8::1".parse::<Address<Any>>()?;
55    ///
56    /// assert!(ipv4_addr.is_ipv4());
57    /// assert!(!ipv6_addr.is_ipv4());
58    /// # Ok::<(), ip::Error>(())
59    /// ```
60    #[must_use]
61    pub const fn is_ipv4(&self) -> bool {
62        matches!(self, Self::Ipv4(_))
63    }
64
65    /// Returns [`true`] if this is an IPv6 address.
66    ///
67    /// # Examples
68    ///
69    /// ``` rust
70    /// use ip::{traits::Address as _, Address, Any};
71    ///
72    /// let ipv4_addr = "192.0.2.1".parse::<Address<Any>>()?;
73    /// let ipv6_addr = "2001:db8::1".parse::<Address<Any>>()?;
74    ///
75    /// assert!(!ipv4_addr.is_ipv6());
76    /// assert!(ipv6_addr.is_ipv6());
77    /// # Ok::<(), ip::Error>(())
78    /// ```
79    #[must_use]
80    pub const fn is_ipv6(&self) -> bool {
81        matches!(self, Self::Ipv6(_))
82    }
83
84    // TODO: move to `traits::Address`
85    /// Convert the address to its canonical representation.
86    ///
87    /// [`Address::Ipv4`] variants are returned unchanged.
88    ///
89    /// [`Address::Ipv6`] variants are handled by converting an IPv4-mapped
90    /// IPv6 address to an [`Address::Ipv4`], and returning an
91    /// [`Address::Ipv6`] otherwise.
92    ///
93    /// # Examples
94    ///
95    /// ``` rust
96    /// use ip::{Address, Any, Ipv6};
97    ///
98    /// let ipv4_mapped = "::ffff:192.0.2.1".parse::<Address<Any>>()?;
99    ///
100    /// assert!(ipv4_mapped.is_ipv6());
101    /// assert!(ipv4_mapped.to_canonical().is_ipv4());
102    /// # Ok::<(), ip::Error>(())
103    /// ```
104    #[allow(clippy::wrong_self_convention)]
105    #[must_use]
106    pub fn to_canonical(&self) -> Self {
107        match self {
108            Self::Ipv4(_) => *self,
109            Self::Ipv6(ipv6_addr) => ipv6_addr.to_canonical(),
110        }
111    }
112}
113
114impl traits::Address for Address {
115    delegate! {
116        fn afi(&self) -> concrete::Afi;
117        fn is_broadcast(&self) -> bool;
118        fn is_link_local(&self) -> bool;
119        fn is_private(&self) -> bool;
120        fn is_reserved(&self) -> bool;
121        fn is_shared(&self) -> bool;
122        fn is_thisnet(&self) -> bool;
123        fn is_benchmarking(&self) -> bool;
124        fn is_documentation(&self) -> bool;
125        fn is_global(&self) -> bool;
126        fn is_loopback(&self) -> bool;
127        fn is_multicast(&self) -> bool;
128        fn is_unicast(&self) -> bool;
129        fn is_unspecified(&self) -> bool;
130        fn is_unique_local(&self) -> bool;
131    }
132}
133
134macro_rules! impl_from_address {
135    ( $( $af:ident ),* $(,)? ) => {
136        $(
137            impl From<concrete::Address<$af>> for Address {
138                fn from(addr: concrete::Address<$af>) -> Self {
139                    Self::$af(addr)
140                }
141            }
142        )*
143    }
144}
145impl_from_address!(Ipv4, Ipv6);
146
147macro_rules! impl_from_primitive {
148    ( $( $af:ident ),* $(,)? ) => {
149        $(
150            impl From<<$af as Afi>::Primitive> for Address {
151                fn from(primitive: <$af as Afi>::Primitive) -> Self {
152                    concrete::Address::<$af>::new(primitive).into()
153                }
154            }
155        )*
156    }
157}
158impl_from_primitive!(Ipv4, Ipv6);
159
160macro_rules! impl_from_octets {
161    ( $( $af:ident ),* $(,)? ) => {
162        $(
163            impl From<<$af as Afi>::Octets> for Address {
164                fn from(octets: <$af as Afi>::Octets) -> Self {
165                    <$af as Afi>::Primitive::from_be_bytes(octets).into()
166                }
167            }
168        )*
169    }
170}
171impl_from_octets!(Ipv4, Ipv6);
172
173impl From<[u16; 8]> for Address {
174    fn from(segments: [u16; 8]) -> Self {
175        <Ipv6 as Afi>::Primitive::from_segments(segments).into()
176    }
177}
178
179#[cfg(feature = "std")]
180impl From<std::net::Ipv4Addr> for Address {
181    fn from(addr: std::net::Ipv4Addr) -> Self {
182        concrete::Address::from(addr).into()
183    }
184}
185
186#[cfg(feature = "std")]
187impl From<std::net::Ipv6Addr> for Address {
188    fn from(addr: std::net::Ipv6Addr) -> Self {
189        concrete::Address::from(addr).into()
190    }
191}
192
193#[cfg(feature = "std")]
194impl From<std::net::IpAddr> for Address {
195    fn from(addr: std::net::IpAddr) -> Self {
196        match addr {
197            std::net::IpAddr::V4(addr) => addr.into(),
198            std::net::IpAddr::V6(addr) => addr.into(),
199        }
200    }
201}
202
203impl PartialOrd for Address {
204    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
205        match (self, other) {
206            (Self::Ipv4(addr), Self::Ipv4(other)) => addr.partial_cmp(other),
207            (Self::Ipv6(addr), Self::Ipv6(other)) => addr.partial_cmp(other),
208            _ => None,
209        }
210    }
211}
212
213macro_rules! impl_partial_cmp {
214    ( $( $af:ident ),* $(,)? ) => {
215        $(
216            impl PartialEq<concrete::Address<$af>> for Address {
217                fn eq(&self, other: &concrete::Address<$af>) -> bool {
218                    if let Self::$af(addr) = self {
219                        addr.eq(other)
220                    } else {
221                        false
222                    }
223                }
224            }
225
226            impl PartialEq<Address> for concrete::Address<$af> {
227                fn eq(&self, other: &Address) -> bool {
228                    other.eq(self)
229                }
230            }
231
232            impl PartialOrd<concrete::Address<$af>> for Address {
233                fn partial_cmp(&self, other: &concrete::Address<$af>) -> Option<Ordering> {
234                    if let Self::$af(addr) = self {
235                        addr.partial_cmp(other)
236                    } else {
237                        None
238                    }
239                }
240            }
241
242            impl PartialOrd<Address> for concrete::Address<$af> {
243                fn partial_cmp(&self, other: &Address) -> Option<Ordering> {
244                    other.partial_cmp(self).map(Ordering::reverse)
245                }
246            }
247        )*
248    }
249}
250impl_partial_cmp!(Ipv4, Ipv6);
251
252impl FromStr for Address {
253    type Err = Error;
254
255    fn from_str(s: &str) -> Result<Self, Self::Err> {
256        <Ipv4 as Afi>::Primitive::parse_addr(s)
257            .map(Self::from)
258            .or_else(|_| <Ipv6 as Afi>::Primitive::parse_addr(s).map(Self::from))
259    }
260}
261
262impl fmt::Display for Address {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        match self {
265            Self::Ipv4(addr) => addr.fmt(f),
266            Self::Ipv6(addr) => addr.fmt(f),
267        }
268    }
269}
270
271impl fmt::Debug for Address {
272    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273        write!(f, "Address<Any>::")?;
274        match self {
275            Self::Ipv4(addr) => write!(f, "Ipv4({addr})"),
276            Self::Ipv6(addr) => write!(f, "Ipv6({addr})"),
277        }
278    }
279}
280
281#[cfg(any(test, feature = "arbitrary"))]
282use proptest::{
283    arbitrary::{any, Arbitrary},
284    prop_oneof,
285    strategy::{BoxedStrategy, Strategy},
286};
287
288#[cfg(any(test, feature = "arbitrary"))]
289impl Arbitrary for Address {
290    type Parameters = ();
291    type Strategy = BoxedStrategy<Self>;
292
293    fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
294        prop_oneof![
295            any::<concrete::Address<Ipv4>>().prop_map(Self::Ipv4),
296            any::<concrete::Address<Ipv6>>().prop_map(Self::Ipv6),
297        ]
298        .boxed()
299    }
300}
301
302#[cfg(test)]
303mod tests {
304    use proptest::proptest;
305
306    use super::*;
307    use crate::traits::Address as _;
308
309    #[cfg(feature = "std")]
310    proptest! {
311        #[test]
312        fn parse_any_display(addr in any::<Address>()) {
313            use std::string::ToString as _;
314            let parsed = addr.to_string().parse::<Address>().unwrap();
315            assert_eq!(addr, parsed);
316        }
317    }
318
319    proptest! {
320        #[test]
321        fn symmetric_eq((a, b) in any::<(Address, Address)>()) {
322            assert_eq!(a.eq(&b), b.eq(&a));
323        }
324
325        #[test]
326        fn symmetric_eq_ipv4(a in any::<Address>(), b in any::<concrete::Address<Ipv4>>()) {
327            assert_eq!(a.eq(&b), b.eq(&a));
328        }
329
330        #[test]
331        fn symmetric_eq_ipv6(a in any::<Address>(), b in any::<concrete::Address<Ipv6>>()) {
332            assert_eq!(a.eq(&b), b.eq(&a));
333        }
334
335        #[test]
336        fn transitive_eq((a, b, c) in any::<(Address, Address, Address)>()) {
337            assert_eq!(a == b && b == c, a == c);
338        }
339
340        #[test]
341        fn transitive_eq_ipv4(
342            (a, c) in any::<(Address, Address)>(),
343            b in any::<concrete::Address<Ipv4>>(),
344        ) {
345            assert_eq!(a == b && b == c, a == c);
346        }
347
348        #[test]
349        fn transitive_eq_ipv6(
350            (a, c) in any::<(Address, Address)>(),
351            b in any::<concrete::Address<Ipv6>>(),
352        ) {
353            assert_eq!(a == b && b == c, a == c);
354        }
355    }
356
357    proptest! {
358        #[test]
359        fn dual_cmp((a, b) in any::<(Address, Address)>()) {
360            assert_eq!(a.partial_cmp(&b), b.partial_cmp(&a).map(Ordering::reverse));
361        }
362
363        #[test]
364        fn dual_cmp_ipv4(a in any::<Address>(), b in any::<concrete::Address<Ipv4>>()) {
365            assert_eq!(a.partial_cmp(&b), b.partial_cmp(&a).map(Ordering::reverse));
366        }
367
368        #[test]
369        fn dual_cmp_ipv6(a in any::<Address>(), b in any::<concrete::Address<Ipv6>>()) {
370            assert_eq!(a.partial_cmp(&b), b.partial_cmp(&a).map(Ordering::reverse));
371        }
372
373        #[test]
374        fn transitive_le((a, b, c) in any::<(Address, Address, Address)>()) {
375            if a < b && b < c {
376                assert!(a < c);
377            }
378        }
379
380        #[test]
381        fn transitive_le_ipv4(
382            (a, c) in any::<(Address, Address)>(),
383            b in any::<concrete::Address<Ipv4>>(),
384        ) {
385            if a < b && b < c {
386                assert!(a < c);
387            }
388        }
389
390        #[test]
391        fn transitive_le_ipv6(
392            (a, c) in any::<(Address, Address)>(),
393            b in any::<concrete::Address<Ipv6>>(),
394        ) {
395            if a < b && b < c {
396                assert!(a < c);
397            }
398        }
399
400        #[test]
401        fn transitive_ge((a, b, c) in any::<(Address, Address, Address)>()) {
402            if a > b && b > c {
403                assert!(a > c);
404            }
405        }
406
407        #[test]
408        fn transitive_ge_ipv4(
409            (a, c) in any::<(Address, Address)>(),
410            b in any::<concrete::Address<Ipv4>>(),
411        ) {
412            if a > b && b > c {
413                assert!(a > c);
414            }
415        }
416
417        #[test]
418        fn transitive_ge_ipv6(
419            (a, c) in any::<(Address, Address)>(),
420            b in any::<concrete::Address<Ipv6>>(),
421        ) {
422            if a > b && b > c {
423                assert!(a > c);
424            }
425        }
426    }
427
428    #[test]
429    fn ipv4_broadcast_is_broadcast() {
430        assert!("255.255.255.255".parse::<Address>().unwrap().is_broadcast());
431    }
432
433    #[test]
434    fn ipv4_unicast_is_not_broadcast() {
435        assert!(!"203.0.113.1".parse::<Address>().unwrap().is_broadcast());
436    }
437
438    #[test]
439    fn ipv6_all_ones_is_not_broadcast() {
440        assert!(!"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
441            .parse::<Address>()
442            .unwrap()
443            .is_broadcast());
444    }
445
446    #[test]
447    fn ipv4_link_local_is_link_local() {
448        assert!("169.254.254.1".parse::<Address>().unwrap().is_link_local());
449    }
450
451    #[test]
452    fn ipv6_link_local_is_link_local() {
453        assert!("fe80::1".parse::<Address>().unwrap().is_link_local());
454    }
455
456    #[test]
457    fn ipv4_unicast_is_not_link_local() {
458        assert!(!"203.0.113.1".parse::<Address>().unwrap().is_link_local());
459    }
460
461    #[test]
462    fn ipv6_localhost_is_not_link_local() {
463        assert!(!"::1".parse::<Address>().unwrap().is_link_local());
464    }
465
466    #[test]
467    fn ipv4_private_is_private() {
468        assert!("172.18.0.1".parse::<Address>().unwrap().is_private());
469    }
470
471    #[test]
472    fn ipv4_unicast_is_not_private() {
473        assert!(!"203.0.113.1".parse::<Address>().unwrap().is_private());
474    }
475
476    #[test]
477    fn ipv6_ula_is_not_private() {
478        assert!(!"fc01::1".parse::<Address>().unwrap().is_private());
479    }
480
481    #[test]
482    fn ipv4_reserved_is_reserved() {
483        assert!("240.0.0.1".parse::<Address>().unwrap().is_reserved());
484    }
485
486    #[test]
487    fn ipv4_broadcast_is_not_reserved() {
488        assert!(!"255.255.255.255".parse::<Address>().unwrap().is_reserved());
489    }
490
491    #[test]
492    fn ipv6_unassigned_is_not_reserved() {
493        assert!(!"4::1".parse::<Address>().unwrap().is_reserved());
494    }
495
496    #[test]
497    fn ipv4_shared_is_shared() {
498        assert!("100.72.1.1".parse::<Address>().unwrap().is_shared());
499    }
500
501    #[test]
502    fn ipv4_unicast_is_not_shared() {
503        assert!(!"192.0.2.1".parse::<Address>().unwrap().is_shared());
504    }
505
506    #[test]
507    fn ipv6_ula_is_not_shared() {
508        assert!(!"fc00::1".parse::<Address>().unwrap().is_shared());
509    }
510
511    #[test]
512    fn ipv4_thisnet_is_thisnet() {
513        assert!("0.255.255.255".parse::<Address>().unwrap().is_thisnet());
514    }
515
516    #[test]
517    fn ipv6_unspecified_is_not_thisnet() {
518        assert!(!"::".parse::<Address>().unwrap().is_thisnet());
519    }
520
521    #[test]
522    fn ipv4_benchmarking_is_benmarking() {
523        assert!("198.19.0.1".parse::<Address>().unwrap().is_benchmarking());
524    }
525
526    #[test]
527    fn ipv6_benchmarking_is_benmarking() {
528        assert!("2001:2::1".parse::<Address>().unwrap().is_benchmarking());
529    }
530
531    #[test]
532    fn ipv4_test_net_2_is_documentation() {
533        assert!("198.51.100.1"
534            .parse::<Address>()
535            .unwrap()
536            .is_documentation());
537    }
538
539    #[test]
540    fn ipv6_documentation_is_documentation() {
541        assert!("2001:db8::1".parse::<Address>().unwrap().is_documentation());
542    }
543
544    #[test]
545    fn ipv4_private_1_is_not_global() {
546        assert!(!"10.254.0.0".parse::<Address>().unwrap().is_global());
547    }
548
549    #[test]
550    fn ipv4_private_2_is_not_global() {
551        assert!(!"192.168.10.65".parse::<Address>().unwrap().is_global());
552    }
553
554    #[test]
555    fn ipv4_private_3_is_not_global() {
556        assert!(!"172.16.10.65".parse::<Address>().unwrap().is_global());
557    }
558
559    #[test]
560    fn ipv6_ula_is_not_global() {
561        assert!(!"fc00::1".parse::<Address>().unwrap().is_global());
562    }
563
564    #[test]
565    fn ipv4_thisnet_is_not_global() {
566        assert!(!"0.1.2.3".parse::<Address>().unwrap().is_global());
567    }
568
569    #[test]
570    fn ipv4_unspecified_is_not_global() {
571        assert!(!"0.0.0.0".parse::<Address>().unwrap().is_global());
572    }
573
574    #[test]
575    fn ipv6_unspecified_is_not_global() {
576        assert!(!"::".parse::<Address>().unwrap().is_global());
577    }
578
579    #[test]
580    fn ipv4_localhost_is_not_global() {
581        assert!(!"127.0.0.1".parse::<Address>().unwrap().is_global());
582    }
583
584    #[test]
585    fn ipv6_localhost_is_not_global() {
586        assert!(!"::1".parse::<Address>().unwrap().is_global());
587    }
588
589    #[test]
590    fn ipv4_link_local_is_not_global() {
591        assert!(!"169.254.45.1".parse::<Address>().unwrap().is_global());
592    }
593
594    #[test]
595    fn ipv6_link_local_is_not_global() {
596        assert!(!"fe80::1".parse::<Address>().unwrap().is_global());
597    }
598
599    #[test]
600    fn ipv4_broadcast_is_not_global() {
601        assert!(!"255.255.255.255".parse::<Address>().unwrap().is_global());
602    }
603
604    #[test]
605    fn ipv4_doc_1_is_not_global() {
606        assert!(!"192.0.2.255".parse::<Address>().unwrap().is_global());
607    }
608
609    #[test]
610    fn ipv4_doc_2_is_not_global() {
611        assert!(!"198.51.100.65".parse::<Address>().unwrap().is_global());
612    }
613
614    #[test]
615    fn ipv4_doc_3_is_not_global() {
616        assert!(!"203.0.113.6".parse::<Address>().unwrap().is_global());
617    }
618
619    #[test]
620    fn ipv6_doc_is_not_global() {
621        assert!(!"2001:db8::1".parse::<Address>().unwrap().is_global());
622    }
623
624    #[test]
625    fn ipv4_shared_is_not_global() {
626        assert!(!"100.100.0.0".parse::<Address>().unwrap().is_global());
627    }
628
629    #[test]
630    fn ipv4_proto_specific_1_is_not_global() {
631        assert!(!"192.0.0.0".parse::<Address>().unwrap().is_global());
632    }
633
634    #[test]
635    fn ipv4_proto_specific_2_is_not_global() {
636        assert!(!"192.0.0.255".parse::<Address>().unwrap().is_global());
637    }
638
639    #[test]
640    fn ipv6_proto_specific_is_not_global() {
641        assert!(!"2001:100::1".parse::<Address>().unwrap().is_global());
642    }
643
644    #[test]
645    fn ipv4_reserved_is_not_global() {
646        assert!(!"250.10.20.30".parse::<Address>().unwrap().is_global());
647    }
648
649    #[test]
650    fn ipv4_benchmarking_is_not_global() {
651        assert!(!"198.18.0.0".parse::<Address>().unwrap().is_global());
652    }
653
654    #[test]
655    fn ipv6_benchmarking_is_not_global() {
656        assert!(!"2001:2::1".parse::<Address>().unwrap().is_global());
657    }
658
659    #[test]
660    fn ipv4_local_multicast_is_not_global() {
661        assert!(!"224.0.0.1".parse::<Address>().unwrap().is_global());
662    }
663
664    #[test]
665    fn ipv4_domain_multicast_is_not_global() {
666        assert!(!"239.0.0.1".parse::<Address>().unwrap().is_global());
667    }
668
669    #[test]
670    fn ipv6_non_global_multicast_is_not_global() {
671        assert!(!"ff08::1".parse::<Address>().unwrap().is_global());
672    }
673
674    #[test]
675    fn ipv4_pcp_anycast_is_global() {
676        assert!("192.0.0.9".parse::<Address>().unwrap().is_global());
677    }
678
679    #[test]
680    fn ipv6_orchidv2_is_global() {
681        assert!("2001:20::1".parse::<Address>().unwrap().is_global());
682    }
683
684    #[test]
685    fn ipv4_global_multicast_is_global() {
686        assert!("224.0.1.1".parse::<Address>().unwrap().is_global());
687    }
688
689    #[test]
690    fn ipv6_global_multicast_is_global() {
691        assert!("ff0e::1".parse::<Address>().unwrap().is_global());
692    }
693
694    #[test]
695    fn ipv4_global_unicast_is_global() {
696        assert!("1.1.1.1".parse::<Address>().unwrap().is_global());
697    }
698
699    #[test]
700    fn ipv6_global_unicast_is_global() {
701        assert!("2606:4700:4700::1111"
702            .parse::<Address>()
703            .unwrap()
704            .is_global());
705    }
706
707    #[test]
708    fn ipv4_loopback_is_loopback() {
709        assert!("127.0.0.53".parse::<Address>().unwrap().is_loopback());
710    }
711
712    #[test]
713    fn ipv6_loopback_is_loopback() {
714        assert!("::1".parse::<Address>().unwrap().is_loopback());
715    }
716
717    #[test]
718    fn ipv4_multicast_is_multicast() {
719        assert!("224.254.0.0".parse::<Address>().unwrap().is_multicast());
720    }
721
722    #[test]
723    fn ipv4_unicast_is_not_multicast() {
724        assert!(!"172.16.10.65".parse::<Address>().unwrap().is_multicast());
725    }
726
727    #[test]
728    fn ipv6_multicast_is_multicast() {
729        assert!("ff01::1".parse::<Address>().unwrap().is_multicast());
730    }
731
732    #[test]
733    fn ipv4_unspecified_is_unspecified() {
734        assert!("0.0.0.0".parse::<Address>().unwrap().is_unspecified());
735    }
736
737    #[test]
738    fn ipv6_unspecified_is_unspecified() {
739        assert!("::".parse::<Address>().unwrap().is_unspecified());
740    }
741
742    #[test]
743    fn ipv6_ula_is_unique_local() {
744        assert!("fc01::1".parse::<Address>().unwrap().is_unique_local());
745    }
746
747    #[test]
748    fn ipv6_doc_is_not_unique_local() {
749        assert!(!"2001:db8::1".parse::<Address>().unwrap().is_unique_local());
750    }
751
752    #[test]
753    fn ipv4_private_is_not_unique_local() {
754        assert!(!"192.168.1.1".parse::<Address>().unwrap().is_unique_local());
755    }
756
757    #[test]
758    fn ipv6_unicast_is_unicast() {
759        assert!("2001:db8::1".parse::<Address>().unwrap().is_unicast());
760    }
761    #[test]
762    fn ipv4_unicast_is_unicast() {
763        assert!("192.168.1.1".parse::<Address>().unwrap().is_unicast());
764    }
765    #[test]
766    fn ipv6_multicast_is_not_unicast() {
767        assert!(!"ffaa::1".parse::<Address>().unwrap().is_unicast());
768    }
769    #[test]
770    fn ipv4_multicast_is_not_unicast() {
771        assert!(!"239.0.0.1".parse::<Address>().unwrap().is_unicast());
772    }
773    #[test]
774    fn ipv4_broadcast_is_not_unicast() {
775        assert!(!"255.255.255.255".parse::<Address>().unwrap().is_unicast());
776    }
777
778    #[test]
779    fn ipv4_unicast_global_is_unicast_global() {
780        assert!("1.1.1.1".parse::<Address>().unwrap().is_unicast_global());
781    }
782    #[test]
783    fn ipv6_unicast_global_is_unicast_global() {
784        assert!("2606:4700:4700::1111"
785            .parse::<Address>()
786            .unwrap()
787            .is_unicast_global());
788    }
789    #[test]
790    fn ipv4_unicast_private_is_not_unicast_global() {
791        assert!(!"192.168.1.1"
792            .parse::<Address>()
793            .unwrap()
794            .is_unicast_global());
795    }
796    #[test]
797    fn ipv4_multicast_global_is_not_unicast_global() {
798        assert!(!"225.0.0.1".parse::<Address>().unwrap().is_unicast_global());
799    }
800    #[test]
801    fn ipv6_unicast_documentation_is_not_unicast_global() {
802        assert!(!"2001:db8::1"
803            .parse::<Address>()
804            .unwrap()
805            .is_unicast_global());
806    }
807    #[test]
808    fn ipv6_multicast_global_is_not_unicast_global() {
809        assert!(!"ff0e::1".parse::<Address>().unwrap().is_unicast_global());
810    }
811}