1use std::cmp::Ordering;
2use std::fmt;
3use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
4use std::str::FromStr;
5use crate::{IpNetworkError, IpNetworkParseError};
6use crate::helpers;
7use crate::{Ipv4Network, Ipv6Network};
8
9#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash, PartialOrd, Ord)]
11pub enum IpNetwork {
12 V4(Ipv4Network),
13 V6(Ipv6Network),
14}
15
16impl IpNetwork {
17 #[allow(clippy::new_ret_no_self)]
35 pub fn new<I: Into<IpAddr>>(network_address: I, netmask: u8) -> Result<Self, IpNetworkError> {
36 Ok(match network_address.into() {
37 IpAddr::V4(ip) => IpNetwork::V4(Ipv4Network::new(ip, netmask)?),
38 IpAddr::V6(ip) => IpNetwork::V6(Ipv6Network::new(ip, netmask)?),
39 })
40 }
41
42 pub fn new_truncate<I: Into<IpAddr>>(
62 network_address: I,
63 netmask: u8,
64 ) -> Result<Self, IpNetworkError> {
65 Ok(match network_address.into() {
66 IpAddr::V4(ip) => IpNetwork::V4(Ipv4Network::new_truncate(ip, netmask)?),
67 IpAddr::V6(ip) => IpNetwork::V6(Ipv6Network::new_truncate(ip, netmask)?),
68 })
69 }
70
71 pub fn network_address(&self) -> IpAddr {
84 match self {
85 IpNetwork::V4(ip_network) => IpAddr::V4(ip_network.network_address()),
86 IpNetwork::V6(ip_network) => IpAddr::V6(ip_network.network_address()),
87 }
88 }
89
90 pub fn netmask(&self) -> u8 {
103 match self {
104 IpNetwork::V4(ip_network) => ip_network.netmask(),
105 IpNetwork::V6(ip_network) => ip_network.netmask(),
106 }
107 }
108
109 pub fn is_ipv4(&self) -> bool {
111 match self {
112 IpNetwork::V4(_) => true,
113 IpNetwork::V6(_) => false,
114 }
115 }
116
117 pub fn is_ipv6(&self) -> bool {
119 !self.is_ipv4()
120 }
121
122 pub fn contains<I: Into<IpAddr>>(&self, ip: I) -> bool {
137 match (self, ip.into()) {
138 (IpNetwork::V4(network), IpAddr::V4(ip)) => network.contains(ip),
139 (IpNetwork::V6(network), IpAddr::V6(ip)) => network.contains(ip),
140 _ => false,
141 }
142 }
143
144 pub fn is_default_route(&self) -> bool {
146 match self {
147 IpNetwork::V4(ip_network) => ip_network.is_default_route(),
148 IpNetwork::V6(ip_network) => ip_network.is_default_route(),
149 }
150 }
151
152 pub fn is_multicast(&self) -> bool {
154 match self {
155 IpNetwork::V4(ip_network) => ip_network.is_multicast(),
156 IpNetwork::V6(ip_network) => ip_network.is_multicast(),
157 }
158 }
159
160 pub fn is_documentation(&self) -> bool {
162 match self {
163 IpNetwork::V4(ip_network) => ip_network.is_documentation(),
164 IpNetwork::V6(ip_network) => ip_network.is_documentation(),
165 }
166 }
167
168 pub fn is_loopback(&self) -> bool {
170 match self {
171 IpNetwork::V4(ip_network) => ip_network.is_loopback(),
172 IpNetwork::V6(ip_network) => ip_network.is_loopback(),
173 }
174 }
175
176 pub fn is_global(&self) -> bool {
178 match self {
179 IpNetwork::V4(ip_network) => ip_network.is_global(),
180 IpNetwork::V6(ip_network) => ip_network.is_global(),
181 }
182 }
183
184 pub fn from_str_truncate(s: &str) -> Result<Self, IpNetworkParseError> {
196 let (ip, netmask) =
197 helpers::split_ip_netmask(s).ok_or(IpNetworkParseError::InvalidFormatError)?;
198
199 let network_address =
200 IpAddr::from_str(ip).map_err(|_| IpNetworkParseError::AddrParseError)?;
201 let netmask =
202 u8::from_str(netmask).map_err(|_| IpNetworkParseError::InvalidNetmaskFormat)?;
203
204 IpNetwork::new_truncate(network_address, netmask)
205 .map_err(IpNetworkParseError::IpNetworkError)
206 }
207
208 pub fn collapse_addresses(addresses: &[Self]) -> Vec<Self> {
210 let mut ipv4_networks = vec![];
211 let mut ipv6_networks = vec![];
212 for address in addresses {
213 match address {
214 IpNetwork::V4(ip_network) => ipv4_networks.push(*ip_network),
215 IpNetwork::V6(ip_network) => ipv6_networks.push(*ip_network),
216 }
217 }
218
219 let mut collapsed = Ipv4Network::collapse_addresses(&ipv4_networks)
220 .into_iter()
221 .map(IpNetwork::from)
222 .collect::<Vec<_>>();
223 collapsed.extend(
224 Ipv6Network::collapse_addresses(&ipv6_networks)
225 .into_iter()
226 .map(IpNetwork::from),
227 );
228 collapsed
229 }
230}
231
232impl fmt::Display for IpNetwork {
233 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246 match *self {
247 IpNetwork::V4(ref network) => network.fmt(f),
248 IpNetwork::V6(ref network) => network.fmt(f),
249 }
250 }
251}
252
253impl FromStr for IpNetwork {
254 type Err = IpNetworkParseError;
255
256 fn from_str(s: &str) -> Result<IpNetwork, IpNetworkParseError> {
269 let (ip, netmask) =
270 helpers::split_ip_netmask(s).ok_or(IpNetworkParseError::InvalidFormatError)?;
271
272 let network_address =
273 IpAddr::from_str(ip).map_err(|_| IpNetworkParseError::AddrParseError)?;
274 let netmask =
275 u8::from_str(netmask).map_err(|_| IpNetworkParseError::InvalidNetmaskFormat)?;
276
277 IpNetwork::new(network_address, netmask).map_err(IpNetworkParseError::IpNetworkError)
278 }
279}
280
281impl From<Ipv4Addr> for IpNetwork {
282 #[inline]
284 fn from(ip: Ipv4Addr) -> Self {
285 IpNetwork::V4(Ipv4Network::from(ip))
286 }
287}
288
289impl From<Ipv6Addr> for IpNetwork {
290 #[inline]
292 fn from(ip: Ipv6Addr) -> Self {
293 IpNetwork::V6(Ipv6Network::from(ip))
294 }
295}
296
297impl From<IpAddr> for IpNetwork {
298 fn from(ip: IpAddr) -> Self {
300 match ip {
301 IpAddr::V4(ip) => IpNetwork::from(ip),
302 IpAddr::V6(ip) => IpNetwork::from(ip),
303 }
304 }
305}
306
307impl From<Ipv4Network> for IpNetwork {
308 #[inline]
309 fn from(network: Ipv4Network) -> Self {
310 IpNetwork::V4(network)
311 }
312}
313
314impl From<Ipv6Network> for IpNetwork {
315 #[inline]
316 fn from(network: Ipv6Network) -> Self {
317 IpNetwork::V6(network)
318 }
319}
320
321impl PartialEq<Ipv4Network> for IpNetwork {
322 fn eq(&self, other: &Ipv4Network) -> bool {
323 match self {
324 IpNetwork::V4(v4) => v4 == other,
325 IpNetwork::V6(_) => false,
326 }
327 }
328}
329
330impl PartialEq<Ipv6Network> for IpNetwork {
331 fn eq(&self, other: &Ipv6Network) -> bool {
332 match self {
333 IpNetwork::V4(_) => false,
334 IpNetwork::V6(v6) => v6 == other,
335 }
336 }
337}
338
339impl PartialEq<IpNetwork> for Ipv4Network {
340 fn eq(&self, other: &IpNetwork) -> bool {
341 match other {
342 IpNetwork::V4(v4) => self == v4,
343 IpNetwork::V6(_) => false,
344 }
345 }
346}
347
348impl PartialEq<IpNetwork> for Ipv6Network {
349 fn eq(&self, other: &IpNetwork) -> bool {
350 match other {
351 IpNetwork::V4(_) => false,
352 IpNetwork::V6(v6) => self == v6,
353 }
354 }
355}
356
357impl PartialOrd<Ipv4Network> for IpNetwork {
358 fn partial_cmp(&self, other: &Ipv4Network) -> Option<Ordering> {
359 match self {
360 IpNetwork::V4(v4) => v4.partial_cmp(other),
361 IpNetwork::V6(_) => Some(Ordering::Greater),
362 }
363 }
364}
365
366impl PartialOrd<IpNetwork> for Ipv4Network {
367 fn partial_cmp(&self, other: &IpNetwork) -> Option<Ordering> {
368 match other {
369 IpNetwork::V4(v4) => self.partial_cmp(v4),
370 IpNetwork::V6(_) => Some(Ordering::Less),
371 }
372 }
373}
374
375impl PartialOrd<Ipv6Network> for IpNetwork {
376 fn partial_cmp(&self, other: &Ipv6Network) -> Option<Ordering> {
377 match self {
378 IpNetwork::V4(_) => Some(Ordering::Less),
379 IpNetwork::V6(v6) => v6.partial_cmp(other),
380 }
381 }
382}
383
384impl PartialOrd<IpNetwork> for Ipv6Network {
385 fn partial_cmp(&self, other: &IpNetwork) -> Option<Ordering> {
386 match other {
387 IpNetwork::V4(_) => Some(Ordering::Greater),
388 IpNetwork::V6(v6) => self.partial_cmp(v6),
389 }
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
396 use crate::{IpNetwork, IpNetworkParseError, IpNetworkError, Ipv4Network, Ipv6Network};
397 use std::str::FromStr;
398
399 fn return_test_ipv4_network() -> Ipv4Network {
400 Ipv4Network::new(Ipv4Addr::new(192, 168, 0, 0), 16).unwrap()
401 }
402
403 fn return_test_ipv6_network() -> Ipv6Network {
404 Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32).unwrap()
405 }
406
407 #[test]
408 fn network_address_ipv4() {
409 let ip_network = IpNetwork::V4(return_test_ipv4_network());
410 assert_eq!(
411 IpAddr::V4(Ipv4Addr::new(192, 168, 0, 0)),
412 ip_network.network_address()
413 );
414 }
415
416 #[test]
417 fn network_address_ipv6() {
418 let ip_network = IpNetwork::V6(return_test_ipv6_network());
419 assert_eq!(
420 IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)),
421 ip_network.network_address()
422 );
423 }
424
425 #[test]
426 fn is_ipv4() {
427 let ip_network = IpNetwork::V4(return_test_ipv4_network());
428 assert!(ip_network.is_ipv4());
429 assert!(!ip_network.is_ipv6());
430 }
431
432 #[test]
433 fn is_ipv6() {
434 let ip_network = IpNetwork::V6(return_test_ipv6_network());
435 assert!(ip_network.is_ipv6());
436 assert!(!ip_network.is_ipv4());
437 }
438
439 #[test]
440 fn parse_ipv4() {
441 let ip_network: IpNetwork = "192.168.0.0/16".parse().unwrap();
442 assert_eq!(ip_network, IpNetwork::V4(return_test_ipv4_network()));
443 }
444
445 #[test]
446 fn parse_ipv6() {
447 let ip_network: IpNetwork = "2001:db8::/32".parse().unwrap();
448 assert_eq!(ip_network, IpNetwork::V6(return_test_ipv6_network()));
449 }
450
451 #[test]
452 fn parse_empty() {
453 let ip_network = "".parse::<IpNetwork>();
454 assert!(ip_network.is_err());
455 assert_eq!(
456 IpNetworkParseError::InvalidFormatError,
457 ip_network.unwrap_err()
458 );
459 }
460
461 #[test]
462 fn parse_invalid_netmask() {
463 let ip_network = "192.168.0.0/a".parse::<IpNetwork>();
464 assert!(ip_network.is_err());
465 assert_eq!(
466 IpNetworkParseError::InvalidNetmaskFormat,
467 ip_network.unwrap_err()
468 );
469 }
470
471 #[test]
472 fn parse_invalid_ip() {
473 let ip_network = "192.168.0.0a/16".parse::<IpNetwork>();
474 assert!(ip_network.is_err());
475 assert_eq!(IpNetworkParseError::AddrParseError, ip_network.unwrap_err());
476 }
477
478 #[test]
479 fn parse_ipv4_host_bits_set() {
480 let ip_network = "192.168.0.1/16".parse::<IpNetwork>();
481 assert!(ip_network.is_err());
482 assert_eq!(
483 IpNetworkParseError::IpNetworkError(IpNetworkError::HostBitsSet),
484 ip_network.unwrap_err()
485 );
486 }
487
488 #[test]
489 fn parse_ipv6_host_bits_set() {
490 let ip_network = "2001:db8::1/32".parse::<IpNetwork>();
491 assert!(ip_network.is_err());
492 assert_eq!(
493 IpNetworkParseError::IpNetworkError(IpNetworkError::HostBitsSet),
494 ip_network.unwrap_err()
495 );
496 }
497
498 #[test]
499 fn format_ipv4() {
500 let ip_network = IpNetwork::V4(return_test_ipv4_network());
501 assert_eq!(ip_network.to_string(), "192.168.0.0/16");
502 }
503
504 #[test]
505 fn format_ipv6() {
506 let ip_network = IpNetwork::V6(return_test_ipv6_network());
507 assert_eq!(ip_network.to_string(), "2001:db8::/32");
508 }
509
510 #[test]
511 fn from_ipv4addr() {
512 let ipv4addr = Ipv4Addr::new(1, 2, 3, 4);
513 let ip_network = IpNetwork::from(ipv4addr);
514 assert_eq!(IpAddr::V4(ipv4addr), ip_network.network_address());
515 assert_eq!(32, ip_network.netmask());
516 }
517
518 #[test]
519 fn from_ipv6addr() {
520 let ipv6addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0);
521 let ip_network = IpNetwork::from(ipv6addr);
522 assert_eq!(IpAddr::V6(ipv6addr), ip_network.network_address());
523 assert_eq!(128, ip_network.netmask());
524 }
525
526 #[test]
527 fn from_ipaddr() {
528 let ipaddr = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
529 let ip_network = IpNetwork::from(ipaddr);
530 assert_eq!(ipaddr, ip_network.network_address());
531 assert_eq!(128, ip_network.netmask());
532 }
533
534 #[test]
535 fn from_ipv4_network() {
536 let ipv4_network = return_test_ipv4_network();
537 let ip_network = IpNetwork::from(ipv4_network);
538 assert_eq!(
539 IpAddr::V4(ipv4_network.network_address()),
540 ip_network.network_address()
541 );
542 assert_eq!(ipv4_network.netmask(), ip_network.netmask());
543 }
544
545 #[test]
546 fn from_ipv6_network() {
547 let ipv6_network = return_test_ipv6_network();
548 let ip_network = IpNetwork::from(ipv6_network);
549 assert_eq!(
550 IpAddr::V6(ipv6_network.network_address()),
551 ip_network.network_address()
552 );
553 assert_eq!(ipv6_network.netmask(), ip_network.netmask());
554 }
555
556 #[test]
557 fn equal_ip_network_ipv4_network() {
558 let ip_network = IpNetwork::V4(return_test_ipv4_network());
559 let ipv4_network = return_test_ipv4_network();
560 let different = Ipv4Network::new(Ipv4Addr::new(1, 2, 3, 4), 32).unwrap();
561 assert_eq!(ip_network, ipv4_network);
562 assert_eq!(ipv4_network, ip_network);
563 assert_ne!(ip_network, different);
564 assert_ne!(different, ip_network);
565 assert_ne!(ip_network, return_test_ipv6_network());
566 assert_ne!(return_test_ipv6_network(), ip_network);
567 }
568
569 #[test]
570 fn equal_ip_network_ipv6_network() {
571 let ip_network = IpNetwork::V6(return_test_ipv6_network());
572 let ipv6_network = return_test_ipv6_network();
573 let different = Ipv6Network::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 128).unwrap();
574 assert_eq!(ip_network, ipv6_network);
575 assert_eq!(ipv6_network, ip_network);
576 assert_ne!(ip_network, different);
577 assert_ne!(different, ip_network);
578 assert_ne!(ip_network, return_test_ipv4_network());
579 assert_ne!(return_test_ipv4_network(), ip_network);
580 }
581
582 #[test]
583 fn compare_ip_network_ipv4_network_same() {
584 let ip_network = IpNetwork::V4(return_test_ipv4_network());
585 let ipv4_network = return_test_ipv4_network();
586 assert!(ip_network <= ipv4_network);
587 assert!(ip_network >= ipv4_network);
588 assert!(ipv4_network <= ip_network);
589 assert!(ipv4_network >= ip_network);
590 }
591
592 #[test]
593 fn compare_ip_network_ipv6_network_same() {
594 let ip_network = IpNetwork::V6(return_test_ipv6_network());
595 let ipv6_network = return_test_ipv6_network();
596 assert!(ip_network <= ipv6_network);
597 assert!(ip_network >= ipv6_network);
598 assert!(ipv6_network <= ip_network);
599 assert!(ipv6_network >= ip_network);
600 }
601
602 #[test]
603 fn compare_ip_network_v4_ip_network_v6() {
604 let ip_network_v4 = IpNetwork::V4(return_test_ipv4_network());
605 let ip_network_v6 = IpNetwork::V6(return_test_ipv6_network());
606 assert!(ip_network_v4 < ip_network_v6);
607 assert!(ip_network_v6 > ip_network_v4);
608 }
609
610 #[test]
611 fn collapse_addresses() {
612 let addresses: Vec<_> = [
613 "192.0.2.0/25",
614 "192.0.2.128/25",
615 "2001::/100",
616 "2001::/120",
617 "2001::/96",
618 ]
619 .iter()
620 .map(|i| IpNetwork::from_str(i).unwrap())
621 .collect();
622 let collapsed = IpNetwork::collapse_addresses(&addresses);
623 assert_eq!(2, collapsed.len());
624 }
625}