ip/concrete/prefix/
range.rs1use 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 #[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 pub const ALL: Self = Self {
36 prefix: Prefix::DEFAULT,
37 len_range: PrefixLength::MIN..=PrefixLength::MAX,
38 };
39
40 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 pub const fn prefix(&self) -> Prefix<A> {
62 self.prefix
63 }
64
65 pub const fn lower(&self) -> PrefixLength<A> {
67 *self.len_range.start()
68 }
69
70 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 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 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}