ip/any/prefix/
mod.rs

1use core::fmt;
2use core::str::FromStr;
3
4#[cfg(any(test, feature = "arbitrary"))]
5use proptest::{
6    arbitrary::{any_with, Arbitrary, ParamsFor},
7    prop_oneof,
8    strategy::{BoxedStrategy, Strategy},
9};
10
11use super::{delegate, Address, Hostmask, Netmask};
12use crate::{
13    concrete::{self, Ipv4, Ipv6},
14    error::{err, Error, Kind},
15    traits,
16};
17
18mod len;
19pub use self::len::Length;
20
21mod range;
22pub use self::range::Range;
23
24#[cfg(feature = "std")]
25mod set;
26#[cfg(feature = "std")]
27pub use self::set::Set;
28
29mod subprefixes;
30pub use self::subprefixes::Subprefixes;
31
32/// Either an IPv4 or IPv6 prefix.
33///
34/// # Memory Use
35///
36/// Rust enums are sized to accommodate their largest variant, with smaller
37/// variants being padded to fill up any unused space.
38///
39/// As a result, users should avoid using this type in a context where only
40/// [`Prefix::Ipv4`] variants are expected.
41///
42/// # Examples
43///
44/// ``` rust
45/// use ip::{
46///     traits::{Address as _, Prefix as _},
47///     Any, Prefix,
48/// };
49///
50/// let prefix = "192.0.2.0/24".parse::<Prefix<Any>>()?;
51///
52/// assert!(prefix.network().is_documentation());
53/// # Ok::<(), ip::Error>(())
54/// ```
55#[allow(variant_size_differences)]
56#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd)]
57pub enum Prefix {
58    /// IPv4 prefix variant.
59    Ipv4(concrete::Prefix<Ipv4>),
60    /// IPv6 prefix variant.
61    Ipv6(concrete::Prefix<Ipv6>),
62}
63
64impl traits::Prefix for Prefix {
65    type Address = Address;
66    type Length = Length;
67    type Hostmask = Hostmask;
68    type Netmask = Netmask;
69    type Subprefixes = Subprefixes;
70
71    delegate! {
72        fn network(&self) -> Self::Address;
73        fn hostmask(&self) -> Self::Hostmask;
74        fn netmask(&self) -> Self::Netmask;
75        fn max_prefix_len(&self) -> Self::Length;
76        fn prefix_len(&self) -> Self::Length;
77        fn broadcast(&self) -> Self::Address;
78    }
79
80    fn supernet(&self) -> Option<Self> {
81        match self {
82            Self::Ipv4(prefix) => prefix.supernet().map(Self::Ipv4),
83            Self::Ipv6(prefix) => prefix.supernet().map(Self::Ipv6),
84        }
85    }
86
87    fn is_sibling(&self, other: &Self) -> bool {
88        match (self, other) {
89            (Self::Ipv4(prefix), Self::Ipv4(other)) => prefix.is_sibling(other),
90            (Self::Ipv6(prefix), Self::Ipv6(other)) => prefix.is_sibling(other),
91            _ => false,
92        }
93    }
94
95    fn subprefixes(&self, new_prefix_length: Self::Length) -> Result<Self::Subprefixes, Error> {
96        match (self, new_prefix_length) {
97            (Self::Ipv4(prefix), Self::Length::Ipv4(length)) => {
98                prefix.subprefixes(length).map(Self::Subprefixes::Ipv4)
99            }
100            (Self::Ipv6(prefix), Self::Length::Ipv6(length)) => {
101                prefix.subprefixes(length).map(Self::Subprefixes::Ipv6)
102            }
103            _ => Err(err!(Kind::AfiMismatch)),
104        }
105    }
106
107    fn new_prefix_length(&self, length: u8) -> Result<Self::Length, Error> {
108        self.afi().new_prefix_length(length)
109    }
110}
111
112impl From<concrete::Prefix<Ipv4>> for Prefix {
113    fn from(prefix: concrete::Prefix<Ipv4>) -> Self {
114        Self::Ipv4(prefix)
115    }
116}
117
118impl From<concrete::Prefix<Ipv6>> for Prefix {
119    fn from(prefix: concrete::Prefix<Ipv6>) -> Self {
120        Self::Ipv6(prefix)
121    }
122}
123
124impl FromStr for Prefix {
125    type Err = Error;
126
127    fn from_str(s: &str) -> Result<Self, Self::Err> {
128        concrete::Prefix::<Ipv4>::from_str(s)
129            .map(Self::from)
130            .or_else(|_| concrete::Prefix::<Ipv6>::from_str(s).map(Self::from))
131    }
132}
133
134impl fmt::Display for Prefix {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        match self {
137            Self::Ipv4(prefix) => prefix.fmt(f),
138            Self::Ipv6(prefix) => prefix.fmt(f),
139        }
140    }
141}
142
143#[cfg(any(test, feature = "arbitrary"))]
144impl Arbitrary for Prefix {
145    type Parameters = (
146        ParamsFor<concrete::Prefix<Ipv4>>,
147        ParamsFor<concrete::Prefix<Ipv6>>,
148    );
149    type Strategy = BoxedStrategy<Self>;
150
151    fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
152        prop_oneof![
153            any_with::<concrete::Prefix<Ipv4>>(params.0).prop_map(Self::Ipv4),
154            any_with::<concrete::Prefix<Ipv6>>(params.1).prop_map(Self::Ipv6),
155        ]
156        .boxed()
157    }
158}