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#[allow(variant_size_differences)]
44#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd)]
45pub enum Range {
46 Ipv4(concrete::PrefixRange<Ipv4>),
48 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}