Implement is_subnet_of and is_supernet_of

This commit is contained in:
Abhishek Chanda
2019-02-06 23:12:47 +00:00
parent b2c458e6f1
commit 439a0deeb7
3 changed files with 87 additions and 0 deletions

View File

@ -62,6 +62,16 @@ impl Ipv4Network {
self.prefix self.prefix
} }
/// Checks if the given `Ipv4Network` is a subnet of the other.
pub fn is_subnet_of(self, other: Ipv4Network) -> bool {
other.ip() <= self.ip() && other.broadcast() >= self.broadcast()
}
/// Checks if the given `Ipv4Network` is a supernet of the other.
pub fn is_supernet_of(self, other: Ipv4Network) -> bool {
other.is_subnet_of(self)
}
/// Returns the mask for this `Ipv4Network`. /// Returns the mask for this `Ipv4Network`.
/// That means the `prefix` most significant bits will be 1 and the rest 0 /// That means the `prefix` most significant bits will be 1 and the rest 0
/// ///
@ -454,4 +464,35 @@ mod test {
fn assert_sync<T: Sync>() {} fn assert_sync<T: Sync>() {}
assert_sync::<Ipv4Network>(); assert_sync::<Ipv4Network>();
} }
// Tests from cpython https://github.com/python/cpython/blob/e9bc4172d18db9c182d8e04dd7b033097a994c06/Lib/test/test_ipaddress.py
#[test]
fn test_is_subnet_of() {
let mut test_cases: HashMap<(Ipv4Network, Ipv4Network), bool> = HashMap::new();
test_cases.insert(("10.0.0.0/30".parse().unwrap(), "10.0.1.0/24".parse().unwrap()), false);
test_cases.insert(("10.0.0.0/30".parse().unwrap(), "10.0.0.0/24".parse().unwrap()), true);
test_cases.insert(("10.0.0.0/30".parse().unwrap(), "10.0.1.0/24".parse().unwrap()), false);
test_cases.insert(("10.0.1.0/24".parse().unwrap(), "10.0.0.0/30".parse().unwrap()), false);
for (key, val) in test_cases.iter() {
let (src, dest) = (key.0, key.1);
assert_eq!(src.is_subnet_of(dest), *val, "testing with {} and {}", src, dest);
}
}
#[test]
fn test_is_supernet_of() {
let mut test_cases: HashMap<(Ipv4Network, Ipv4Network), bool> = HashMap::new();
test_cases.insert(("10.0.0.0/30".parse().unwrap(), "10.0.1.0/24".parse().unwrap()), false);
test_cases.insert(("10.0.0.0/30".parse().unwrap(), "10.0.0.0/24".parse().unwrap()), false);
test_cases.insert(("10.0.0.0/30".parse().unwrap(), "10.0.1.0/24".parse().unwrap()), false);
test_cases.insert(("10.0.0.0/24".parse().unwrap(), "10.0.0.0/30".parse().unwrap()), true);
for (key, val) in test_cases.iter() {
let (src, dest) = (key.0, key.1);
assert_eq!(src.is_supernet_of(dest), *val, "testing with {} and {}", src, dest);
}
}
} }

View File

@ -111,6 +111,16 @@ impl Ipv6Network {
self.prefix self.prefix
} }
/// Checks if the given `Ipv6Network` is a subnet of the other.
pub fn is_subnet_of(self, other: Ipv6Network) -> bool {
other.ip() <= self.ip() && other.broadcast() >= self.broadcast()
}
/// Checks if the given `Ipv6Network` is a supernet of the other.
pub fn is_supernet_of(self, other: Ipv6Network) -> bool {
other.is_subnet_of(self)
}
/// Returns the mask for this `Ipv6Network`. /// Returns the mask for this `Ipv6Network`.
/// That means the `prefix` most significant bits will be 1 and the rest 0 /// That means the `prefix` most significant bits will be 1 and the rest 0
/// ///
@ -265,6 +275,7 @@ pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use std::collections::HashMap;
use std::net::Ipv6Addr; use std::net::Ipv6Addr;
#[test] #[test]
@ -436,4 +447,35 @@ mod test {
fn assert_sync<T: Sync>() {} fn assert_sync<T: Sync>() {}
assert_sync::<Ipv6Network>(); assert_sync::<Ipv6Network>();
} }
// Tests from cpython https://github.com/python/cpython/blob/e9bc4172d18db9c182d8e04dd7b033097a994c06/Lib/test/test_ipaddress.py
#[test]
fn test_is_subnet_of() {
let mut test_cases: HashMap<(Ipv6Network, Ipv6Network), bool> = HashMap::new();
test_cases.insert(("2000:999::/56".parse().unwrap(), "2000:aaa::/48".parse().unwrap()), false);
test_cases.insert(("2000:aaa::/56".parse().unwrap(), "2000:aaa::/48".parse().unwrap()), true);
test_cases.insert(("2000:bbb::/56".parse().unwrap(), "2000:aaa::/48".parse().unwrap()), false);
test_cases.insert(("2000:aaa::/48".parse().unwrap(), "2000:aaa::/56".parse().unwrap()), false);
for (key, val) in test_cases.iter() {
let (src, dest) = (key.0, key.1);
assert_eq!(src.is_subnet_of(dest), *val, "testing with {} and {}", src, dest);
}
}
#[test]
fn test_is_supernet_of() {
let mut test_cases: HashMap<(Ipv6Network, Ipv6Network), bool> = HashMap::new();
test_cases.insert(("2000:999::/56".parse().unwrap(), "2000:aaa::/48".parse().unwrap()), false);
test_cases.insert(("2000:aaa::/56".parse().unwrap(), "2000:aaa::/48".parse().unwrap()), false);
test_cases.insert(("2000:bbb::/56".parse().unwrap(), "2000:aaa::/48".parse().unwrap()), false);
test_cases.insert(("2000:aaa::/48".parse().unwrap(), "2000:aaa::/56".parse().unwrap()), true);
for (key, val) in test_cases.iter() {
let (src, dest) = (key.0, key.1);
assert_eq!(src.is_supernet_of(dest), *val, "testing with {} and {}", src, dest);
}
}
} }

View File

@ -200,6 +200,10 @@ impl IpNetwork {
} }
} }
// TODO(abhishek) when TryFrom is stable, implement it for IpNetwork to
// variant conversions. Then use that to implement a generic is_subnet_of
// and is_supernet_of
/// Checks if a given `IpAddr` is in this `IpNetwork` /// Checks if a given `IpAddr` is in this `IpNetwork`
/// ///
/// # Examples /// # Examples