ip/any/prefix/
range.rs

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