1use std::fmt::{self, Debug, Display};
50use std::hash::Hash;
51use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
52use std::str::FromStr;
53
54use crate::utils::{
55 ip_addr_to_bit_length, ip_addr_trailing_zeros, MathLog2, IPV4_RESERVED, IPV6_RESERVED,
56};
57use num_traits::{Bounded, NumAssignOps, NumCast, PrimInt, WrappingAdd, Zero};
58
59#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
64pub struct Ipv4Range(u32, u32);
65
66#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
71pub struct Ipv6Range(u128, u128);
72
73#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
78pub enum EitherIpRange {
79 V4(Ipv4Range),
80 V6(Ipv6Range),
81}
82
83impl EitherIpRange {
84 pub fn into_v4(self) -> Option<Ipv4Range> {
86 match self {
87 EitherIpRange::V4(r) => Some(r),
88 _ => None,
89 }
90 }
91
92 pub fn into_v6(self) -> Option<Ipv6Range> {
94 match self {
95 EitherIpRange::V6(r) => Some(r),
96 _ => None,
97 }
98 }
99
100 pub fn is_v4(self) -> bool {
102 matches!(self, EitherIpRange::V4(_))
103 }
104
105 pub fn is_v6(self) -> bool {
107 matches!(self, EitherIpRange::V6(_))
108 }
109}
110
111impl From<Ipv4Range> for EitherIpRange {
112 fn from(r: Ipv4Range) -> Self {
113 EitherIpRange::V4(r)
114 }
115}
116
117impl From<Ipv6Range> for EitherIpRange {
118 fn from(r: Ipv6Range) -> Self {
119 EitherIpRange::V6(r)
120 }
121}
122
123impl FromStr for EitherIpRange {
124 type Err = ();
125
126 fn from_str(s: &str) -> Result<EitherIpRange, Self::Err> {
127 if let Some((ip, cidr)) = s.split_once("/").or(Some((s, ""))) {
128 let ip = ip.parse::<IpAddr>().map_err(|_| ())?;
129 let cidr = if cidr.is_empty() {
130 ip_addr_to_bit_length(ip) as u8
131 } else {
132 cidr.parse::<u8>().map_err(|_| ())?
133 };
134 if cidr as u32 > ip_addr_to_bit_length(ip)
135 || ip_addr_trailing_zeros(ip) < ip_addr_to_bit_length(ip) - cidr as u32
136 {
137 return Err(()); }
139 Ok(match ip {
140 IpAddr::V4(ip) => EitherIpRange::V4(Ipv4Range::from_cidr_pair((ip, cidr))),
141 IpAddr::V6(ip) => EitherIpRange::V6(Ipv6Range::from_cidr_pair((ip, cidr))),
142 })
143 } else {
144 Err(())
145 }
146 }
147}
148
149pub trait IpRange: Copy + Eq + Ord + Display + Debug + Hash + 'static {
158 type Address;
159 type AddressDecimal: PrimInt + NumAssignOps + WrappingAdd + Bounded + Display + Debug;
160
161 fn first_address(&self) -> Self::Address;
162 fn first_address_as_decimal(&self) -> Self::AddressDecimal;
163 fn last_address(&self) -> Self::Address;
164 fn last_address_as_decimal(&self) -> Self::AddressDecimal;
165 fn length(&self) -> Self::AddressDecimal;
166 fn from_cidr_pair(first_address_and_cidr: (Self::Address, u8)) -> Self;
167 fn into_cidr_pair(self) -> (Self::Address, u8);
168 fn from_cidr_pair_decimal(
169 first_and_last_address_decimal: (Self::AddressDecimal, Self::AddressDecimal),
170 ) -> Self;
171 fn into_cidr_pair_decimal(self) -> (Self::AddressDecimal, Self::AddressDecimal);
172
173 fn full() -> Self {
175 Self::from_cidr_pair_decimal((
176 Self::AddressDecimal::zero(),
177 Self::AddressDecimal::max_value(),
178 ))
179 }
180
181 fn reserved() -> &'static [Self];
183}
184
185macro_rules! impl_ip_range {
186 ($ip_range: ident, $address_type: ident, $decimal_type: ident, $reserved: ident) => {
187 #[allow(clippy::legacy_numeric_constants)]
188 impl IpRange for $ip_range {
189 type Address = $address_type;
190 type AddressDecimal = $decimal_type;
191
192 fn first_address(&self) -> Self::Address {
193 self.0.into()
194 }
195
196 fn first_address_as_decimal(&self) -> Self::AddressDecimal {
197 self.0
198 }
199
200 fn last_address(&self) -> Self::Address {
201 self.1.into()
202 }
203
204 fn last_address_as_decimal(&self) -> Self::AddressDecimal {
205 self.1
206 }
207
208 fn length(&self) -> Self::AddressDecimal {
209 (self.1 - self.0).wrapping_add(1 as $decimal_type)
210 }
211
212 fn from_cidr_pair(first_address_and_cidr: (Self::Address, u8)) -> Self {
213 let first: $decimal_type = first_address_and_cidr.0.into();
214 let last = if first_address_and_cidr.1 == 0 {
215 Self::AddressDecimal::max_value()
216 } else {
217 first
218 + (<Self::AddressDecimal as NumCast>::from(2).unwrap().pow(
219 std::mem::size_of::<$address_type>() as u32 * 8
220 - first_address_and_cidr.1 as u32,
221 ) - 1)
222 };
223 Self(first, last)
224 }
225
226 fn into_cidr_pair(self) -> (Self::Address, u8) {
227 self.into()
228 }
229
230 fn from_cidr_pair_decimal(
231 first_and_last_address_decimal: (Self::AddressDecimal, Self::AddressDecimal),
232 ) -> Self {
233 assert!(first_and_last_address_decimal.0 <= first_and_last_address_decimal.1);
234 Self(
235 first_and_last_address_decimal.0,
236 first_and_last_address_decimal.1,
237 )
238 }
239
240 fn into_cidr_pair_decimal(self) -> (Self::AddressDecimal, Self::AddressDecimal) {
241 self.into()
242 }
243
244 fn reserved() -> &'static [Self] {
245 &$reserved[..]
246 }
247 }
248
249 impl From<($address_type, u8)> for $ip_range {
250 fn from(first_address_and_cidr: ($address_type, u8)) -> $ip_range {
251 Self::from_cidr_pair(first_address_and_cidr)
252 }
253 }
254
255 impl From<$ip_range> for ($address_type, u8) {
256 fn from(range: $ip_range) -> ($address_type, u8) {
257 (
258 $address_type::from(range.0),
259 (range.1 - range.0 + 1)
260 .checked_log2()
261 .expect("Range not normalize yet") as u8,
262 )
263 }
264 }
265
266 impl From<($decimal_type, $decimal_type)> for $ip_range {
267 fn from(first_and_last_address_decimal: ($decimal_type, $decimal_type)) -> $ip_range {
268 Self::from_cidr_pair_decimal(first_and_last_address_decimal)
269 }
270 }
271
272 impl From<$ip_range> for ($decimal_type, $decimal_type) {
273 fn from(range: $ip_range) -> ($decimal_type, $decimal_type) {
274 (range.0.into(), range.1)
275 }
276 }
277
278 impl fmt::Display for $ip_range {
283 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
284 let cidr = if self.first_address_as_decimal() == $decimal_type::zero()
285 && self.length() == 0
286 {
287 0
288 } else {
289 std::mem::size_of::<$decimal_type>() as u32 * 8
290 - self
291 .length()
292 .checked_log2()
293 .expect("Range not normalize yet")
294 };
295 write!(f, "{}/{}", self.first_address(), cidr)
296 }
297 }
298 };
299}
300
301impl_ip_range!(Ipv4Range, Ipv4Addr, u32, IPV4_RESERVED);
302impl_ip_range!(Ipv6Range, Ipv6Addr, u128, IPV6_RESERVED);
303
304pub mod aggregator;
305pub mod parser;
306mod utils;
307
308pub use aggregator::Aggregator;
309pub use parser::parse_cidrs;
310
311#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
312mod wasm;
313
314#[cfg(test)]
315mod tests;