ip/concrete/prefix/
range.rs

1use core::cmp::{max, min, Ordering};
2use core::fmt;
3use core::ops::RangeInclusive;
4use core::str::FromStr;
5
6#[cfg(any(test, feature = "arbitrary"))]
7use proptest::{
8    arbitrary::{any_with, Arbitrary, ParamsFor},
9    strategy::{BoxedStrategy, Just, Strategy},
10};
11
12use super::{impl_try_from_any, Address, Ipv4, Ipv6, Prefix, PrefixLength, Subprefixes};
13#[cfg(any(test, feature = "arbitrary"))]
14use crate::traits::primitive;
15use crate::{
16    any,
17    error::{err, Error, Kind},
18    traits::{self, primitive::Address as _, Afi, Prefix as _, PrefixLength as _},
19};
20
21mod private {
22    #[allow(clippy::wildcard_imports)]
23    use super::*;
24
25    /// A set of [`Prefix<A>`] covered by a common super-prefix, each having a
26    /// pref-length within a contigious range.
27    #[derive(Clone, Debug, Hash, PartialEq, Eq)]
28    pub struct Range<A: Afi> {
29        prefix: Prefix<A>,
30        len_range: RangeInclusive<PrefixLength<A>>,
31    }
32
33    impl<A: Afi> Range<A> {
34        /// The range containing all prefixes of address family `A`.
35        pub const ALL: Self = Self {
36            prefix: Prefix::DEFAULT,
37            len_range: PrefixLength::MIN..=PrefixLength::MAX,
38        };
39
40        /// Construct a new [`Self`] from a convering [`Prefix<A>`] and an
41        /// inclusive range of [`PrefixLength`]..
42        ///
43        /// # Errors
44        ///
45        /// Construction will fail if either:
46        ///
47        /// - `prefix.length() > len_range.start()`; or
48        /// - `len_range.start() > len_range.end()`
49        pub fn new(
50            prefix: Prefix<A>,
51            len_range: RangeInclusive<PrefixLength<A>>,
52        ) -> Result<Self, Error> {
53            if &prefix.length() <= len_range.start() && len_range.start() <= len_range.end() {
54                Ok(Self { prefix, len_range })
55            } else {
56                Err(err!(Kind::PrefixLengthRange))
57            }
58        }
59
60        /// Return the covering super-prefix of `self`.
61        pub const fn prefix(&self) -> Prefix<A> {
62            self.prefix
63        }
64
65        /// Return the lower bound [`PrefixLength`] of `self`.
66        pub const fn lower(&self) -> PrefixLength<A> {
67            *self.len_range.start()
68        }
69
70        /// Return the upper bound [`PrefixLength`] of `self`.
71        pub const fn upper(&self) -> PrefixLength<A> {
72            *self.len_range.end()
73        }
74    }
75}
76
77pub use self::private::Range;
78
79impl<A: Afi> traits::PrefixRange for Range<A> {
80    type Prefix = Prefix<A>;
81    type Length = PrefixLength<A>;
82
83    fn prefix(&self) -> Self::Prefix {
84        self.prefix()
85    }
86
87    fn lower(&self) -> Self::Length {
88        self.lower()
89    }
90
91    fn upper(&self) -> Self::Length {
92        self.upper()
93    }
94
95    fn with_intersection(self, len_range: RangeInclusive<Self::Length>) -> Option<Self> {
96        let lower = max(self.lower(), *len_range.start());
97        let upper = min(self.upper(), *len_range.end());
98        Self::new(self.prefix(), lower..=upper).ok()
99    }
100
101    fn with_length_range(self, len_range: RangeInclusive<Self::Length>) -> Option<Self> {
102        let lower = max(self.lower(), *len_range.start());
103        let upper = *len_range.end();
104        Self::new(self.prefix(), lower..=upper).ok()
105    }
106}
107
108#[allow(clippy::fallible_impl_from)]
109impl<A: Afi> From<Prefix<A>> for Range<A> {
110    fn from(prefix: Prefix<A>) -> Self {
111        // OK to unwrap here as we can guarantee the checks in `new()` will
112        // pass.
113        Self::new(prefix, prefix.length()..=prefix.length()).unwrap()
114    }
115}
116
117impl<A: Afi> FromStr for Range<A> {
118    type Err = Error;
119
120    fn from_str(s: &str) -> Result<Self, Self::Err> {
121        A::Primitive::parse_range(s).and_then(|(addr, len, l, u)| {
122            let (lower, upper) = (
123                PrefixLength::from_primitive(l)?,
124                PrefixLength::from_primitive(u)?,
125            );
126            Self::new(
127                Prefix::new(Address::new(addr), PrefixLength::from_primitive(len)?),
128                lower..=upper,
129            )
130        })
131    }
132}
133
134impl_try_from_any! {
135    any::PrefixRange {
136        any::PrefixRange::Ipv4 => Range<Ipv4>,
137        any::PrefixRange::Ipv6 => Range<Ipv6>,
138    }
139}
140
141impl<A: Afi> fmt::Display for Range<A> {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        write!(f, "{}^{}-{}", self.prefix(), self.lower(), self.upper())
144    }
145}
146
147impl<A: Afi> PartialOrd for Range<A> {
148    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
149        match self.prefix().partial_cmp(&other.prefix()) {
150            _ if self == other => Some(Ordering::Equal),
151            Some(Ordering::Less | Ordering::Equal)
152                if other.lower() <= self.lower() && self.upper() <= other.upper() =>
153            {
154                Some(Ordering::Less)
155            }
156            Some(Ordering::Greater | Ordering::Equal)
157                if self.lower() <= other.lower() && other.upper() <= self.upper() =>
158            {
159                Some(Ordering::Greater)
160            }
161            _ => None,
162        }
163    }
164}
165
166impl<A: Afi> IntoIterator for Range<A> {
167    type Item = Prefix<A>;
168    type IntoIter = IntoIter<A>;
169
170    fn into_iter(self) -> Self::IntoIter {
171        // Safe to unwrap here since we are passing self.lower() which is
172        // guaranteed to be within the bounds check.
173        let current_iter = Some(self.prefix().subprefixes(self.lower()).unwrap());
174        Self::IntoIter {
175            base: self.prefix(),
176            current_length: self.lower(),
177            upper_length: self.upper(),
178            current_iter,
179        }
180    }
181}
182
183#[derive(Debug, Clone)]
184pub struct IntoIter<A: Afi> {
185    base: Prefix<A>,
186    current_length: PrefixLength<A>,
187    upper_length: PrefixLength<A>,
188    current_iter: Option<Subprefixes<A>>,
189}
190
191impl<A: Afi> Iterator for IntoIter<A> {
192    type Item = Prefix<A>;
193
194    fn next(&mut self) -> Option<Self::Item> {
195        loop {
196            if let Some(mut subprefixes) = self.current_iter.take() {
197                if let Some(prefix) = subprefixes.next() {
198                    self.current_iter = Some(subprefixes);
199                    break Some(prefix);
200                }
201                self.current_length = self
202                    .current_length
203                    .increment()
204                    .ok()
205                    .filter(|length| length <= &self.upper_length)?;
206                self.current_iter = self.base.subprefixes(self.current_length).ok();
207            } else {
208                break None;
209            }
210        }
211    }
212}
213
214#[cfg(any(test, feature = "arbitrary"))]
215impl<A> Arbitrary for Range<A>
216where
217    A: Afi + 'static,
218    A::Primitive: Arbitrary,
219    RangeInclusive<<A::Primitive as primitive::Address<A>>::Length>:
220        Strategy<Value = <A::Primitive as primitive::Address<A>>::Length>,
221{
222    type Parameters = ParamsFor<Prefix<A>>;
223    type Strategy = BoxedStrategy<Self>;
224
225    fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
226        any_with::<Prefix<A>>(params)
227            .prop_flat_map(|prefix| {
228                (
229                    Just(prefix),
230                    (prefix.length().into_primitive()
231                        ..=<A::Primitive as primitive::Address<A>>::MAX_LENGTH)
232                        .prop_flat_map(|lower| {
233                            (
234                                Just(lower),
235                                lower..=<A::Primitive as primitive::Address<A>>::MAX_LENGTH,
236                            )
237                        })
238                        .prop_map(|(lower, upper)| {
239                            <A as traits::AfiClass>::PrefixLength::from_primitive(lower).unwrap()
240                                ..=<A as traits::AfiClass>::PrefixLength::from_primitive(upper)
241                                    .unwrap()
242                        }),
243                )
244            })
245            .prop_map(|(prefix, len_range)| Self::new(prefix, len_range).unwrap())
246            .boxed()
247    }
248}