11 Commits

Author SHA1 Message Date
c3af07d69a Impl From<Ipv4Addr, Ipv6Addr> for IpNetwork (#218) 2025-03-22 15:30:10 -05:00
35977adc81 Iterator size hints (#217)
* feat: iterator size hints

* fix: add size_hint to IpNetworkIterator
2025-02-28 10:00:56 -06:00
0deb2abd8b Add ip_mask_to_prefix_checked (#214)
* Add `ipv4_mask_to_prefix_checked`

* Add `ipv6_mask_to_prefix_checked`

* Add `ip_mask_to_prefix_checked`
2025-01-12 22:04:28 -06:00
170fc4ca6b Add rust-version to Cargo.toml and set MSRV to 1.80.0 (#215) 2025-01-12 22:03:05 -06:00
aa65f088b1 Make IpNetwork::is_ipv4 and IpNetwork::is_ipv6 const (#213) 2025-01-09 13:06:22 -06:00
5f929258ef Make IpNetwork::ip const (#212) 2025-01-08 17:38:34 -06:00
a21f5df04e Make IpNetwork::broadcast const (#211) 2025-01-08 17:38:16 -06:00
876f882523 Make IpNetwork::network const (#210) 2025-01-08 17:37:32 -06:00
49f07a4838 Make IpNetwork::prefix const (#209) 2025-01-08 17:36:53 -06:00
2f219a2ada Make IpNetwork::contains const (#208)
* Make `Ipv6Network::contains` const

* Make `Ipv4Network::contains` const

* Make `IpNetwork::contains` const
2025-01-08 10:22:33 -06:00
9ae719637d Make IpNetwork::mask const (#207)
* Make `Ipv4Network::mask` const

* Make `Ipv6Network::mask` const

* Make `IpNetwork::mask` const
2025-01-07 21:03:10 -06:00
4 changed files with 165 additions and 55 deletions

View File

@ -9,6 +9,7 @@ keywords = ["network", "ip", "address", "cidr"]
readme = "README.md" readme = "README.md"
categories = ["network-programming", "parser-implementations"] categories = ["network-programming", "parser-implementations"]
edition = "2021" edition = "2021"
rust-version = "1.80.0"
[dependencies] [dependencies]
serde = { version = "1.0.200", optional = true } serde = { version = "1.0.200", optional = true }

View File

@ -140,11 +140,11 @@ impl Ipv4Network {
} }
} }
pub fn ip(self) -> Ipv4Addr { pub const fn ip(self) -> Ipv4Addr {
self.addr self.addr
} }
pub fn prefix(self) -> u8 { pub const fn prefix(self) -> u8 {
self.prefix self.prefix
} }
@ -180,13 +180,13 @@ impl Ipv4Network {
/// let net: Ipv4Network = "127.0.0.0/16".parse().unwrap(); /// let net: Ipv4Network = "127.0.0.0/16".parse().unwrap();
/// assert_eq!(net.mask(), Ipv4Addr::new(255, 255, 0, 0)); /// assert_eq!(net.mask(), Ipv4Addr::new(255, 255, 0, 0));
/// ``` /// ```
pub fn mask(&self) -> Ipv4Addr { pub const fn mask(&self) -> Ipv4Addr {
debug_assert!(self.prefix <= 32); debug_assert!(self.prefix <= 32);
if self.prefix == 0 { if self.prefix == 0 {
return Ipv4Addr::new(0, 0, 0, 0); return Ipv4Addr::new(0, 0, 0, 0);
} }
let mask = u32::MAX << (IPV4_BITS - self.prefix); let mask = u32::MAX << (IPV4_BITS - self.prefix);
Ipv4Addr::from(mask) Ipv4Addr::from_bits(mask)
} }
/// Returns the address of the network denoted by this `Ipv4Network`. /// Returns the address of the network denoted by this `Ipv4Network`.
@ -201,10 +201,10 @@ impl Ipv4Network {
/// let net: Ipv4Network = "10.1.9.32/16".parse().unwrap(); /// let net: Ipv4Network = "10.1.9.32/16".parse().unwrap();
/// assert_eq!(net.network(), Ipv4Addr::new(10, 1, 0, 0)); /// assert_eq!(net.network(), Ipv4Addr::new(10, 1, 0, 0));
/// ``` /// ```
pub fn network(&self) -> Ipv4Addr { pub const fn network(&self) -> Ipv4Addr {
let mask = u32::from(self.mask()); let mask = self.mask().to_bits();
let ip = u32::from(self.addr) & mask; let ip = self.addr.to_bits() & mask;
Ipv4Addr::from(ip) Ipv4Addr::from_bits(ip)
} }
/// Returns the broadcasting address of this `Ipv4Network`. /// Returns the broadcasting address of this `Ipv4Network`.
@ -219,10 +219,10 @@ impl Ipv4Network {
/// let net: Ipv4Network = "10.9.0.32/16".parse().unwrap(); /// let net: Ipv4Network = "10.9.0.32/16".parse().unwrap();
/// assert_eq!(net.broadcast(), Ipv4Addr::new(10, 9, 255, 255)); /// assert_eq!(net.broadcast(), Ipv4Addr::new(10, 9, 255, 255));
/// ``` /// ```
pub fn broadcast(&self) -> Ipv4Addr { pub const fn broadcast(&self) -> Ipv4Addr {
let mask = u32::from(self.mask()); let mask = self.mask().to_bits();
let broadcast = u32::from(self.addr) | !mask; let broadcast = self.addr.to_bits() | !mask;
Ipv4Addr::from(broadcast) Ipv4Addr::from_bits(broadcast)
} }
/// Checks if a given `Ipv4Addr` is in this `Ipv4Network` /// Checks if a given `Ipv4Addr` is in this `Ipv4Network`
@ -238,12 +238,12 @@ impl Ipv4Network {
/// assert!(!net.contains(Ipv4Addr::new(127, 0, 1, 70))); /// assert!(!net.contains(Ipv4Addr::new(127, 0, 1, 70)));
/// ``` /// ```
#[inline] #[inline]
pub fn contains(&self, ip: Ipv4Addr) -> bool { pub const fn contains(&self, ip: Ipv4Addr) -> bool {
debug_assert!(self.prefix <= IPV4_BITS); debug_assert!(self.prefix <= IPV4_BITS);
let mask = !(0xffff_ffff_u64 >> self.prefix) as u32; let mask = !(0xffff_ffff_u64 >> self.prefix) as u32;
let net = u32::from(self.addr) & mask; let net = self.addr.to_bits() & mask;
(u32::from(ip) & mask) == net (ip.to_bits() & mask) == net
} }
/// Returns number of possible host addresses in this `Ipv4Network`. /// Returns number of possible host addresses in this `Ipv4Network`.
@ -368,6 +368,15 @@ impl Iterator for Ipv4NetworkIterator {
}; };
Some(next.into()) Some(next.into())
} }
fn size_hint(&self) -> (usize, Option<usize>) {
if let Some(n) = self.next {
let elms = (self.end - n + 1) as usize;
(elms, Some(elms))
} else {
(0, None)
}
}
} }
impl IntoIterator for &'_ Ipv4Network { impl IntoIterator for &'_ Ipv4Network {
@ -382,13 +391,24 @@ impl IntoIterator for &'_ Ipv4Network {
/// ///
/// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`. /// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`.
pub fn ipv4_mask_to_prefix(mask: Ipv4Addr) -> Result<u8, IpNetworkError> { pub fn ipv4_mask_to_prefix(mask: Ipv4Addr) -> Result<u8, IpNetworkError> {
let mask = u32::from(mask); match ipv4_mask_to_prefix_checked(mask) {
Some(prefix) => Ok(prefix),
None => Err(IpNetworkError::InvalidPrefix),
}
}
/// Converts a `Ipv4Addr` network mask into a prefix.
///
/// If the mask is invalid this will return `None`. This is useful in const contexts where
/// [`Option::unwrap`] may be called to trigger a compile-time error if the prefix is invalid.
pub const fn ipv4_mask_to_prefix_checked(mask: Ipv4Addr) -> Option<u8> {
let mask = mask.to_bits();
let prefix = (!mask).leading_zeros() as u8; let prefix = (!mask).leading_zeros() as u8;
if (u64::from(mask) << prefix) & 0xffff_ffff != 0 { if ((mask as u64) << prefix) & 0xffff_ffff != 0 {
Err(IpNetworkError::InvalidPrefix) None
} else { } else {
Ok(prefix) Some(prefix)
} }
} }
@ -571,6 +591,25 @@ mod test {
assert_eq!(None, iter.next()); assert_eq!(None, iter.next());
} }
#[test]
fn iterator_v4_size_hint() {
let cidr: Ipv4Network = "192.168.0.0/24".parse().unwrap();
let mut iter = cidr.iter();
assert_eq!((256, Some(256)), iter.size_hint());
iter.next();
assert_eq!((255, Some(255)), iter.size_hint());
let cidr: Ipv4Network = "192.168.0.0/32".parse().unwrap();
let mut iter = cidr.iter();
assert_eq!((1, Some(1)), iter.size_hint());
iter.next();
assert_eq!((0, None), iter.size_hint());
let cidr: Ipv4Network = "192.168.0.0/0".parse().unwrap();
let iter = cidr.iter();
assert_eq!((4294967295, Some(4294967295)), iter.size_hint());
}
#[test] #[test]
fn v4_mask_to_prefix() { fn v4_mask_to_prefix() {
let mask = Ipv4Addr::new(255, 255, 255, 128); let mask = Ipv4Addr::new(255, 255, 255, 128);

View File

@ -160,11 +160,11 @@ impl Ipv6Network {
} }
} }
pub fn ip(&self) -> Ipv6Addr { pub const fn ip(&self) -> Ipv6Addr {
self.addr self.addr
} }
pub fn prefix(&self) -> u8 { pub const fn prefix(&self) -> u8 {
self.prefix self.prefix
} }
@ -200,14 +200,14 @@ impl Ipv6Network {
/// let net: Ipv6Network = "ff01::0/32".parse().unwrap(); /// let net: Ipv6Network = "ff01::0/32".parse().unwrap();
/// assert_eq!(net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0)); /// assert_eq!(net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0));
/// ``` /// ```
pub fn mask(&self) -> Ipv6Addr { pub const fn mask(&self) -> Ipv6Addr {
debug_assert!(self.prefix <= IPV6_BITS); debug_assert!(self.prefix <= IPV6_BITS);
if self.prefix == 0 { if self.prefix == 0 {
return Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); return Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
} }
let mask = u128::MAX << (IPV6_BITS - self.prefix); let mask = u128::MAX << (IPV6_BITS - self.prefix);
Ipv6Addr::from(mask) Ipv6Addr::from_bits(mask)
} }
/// Returns the address of the network denoted by this `Ipv6Network`. /// Returns the address of the network denoted by this `Ipv6Network`.
@ -222,10 +222,10 @@ impl Ipv6Network {
/// let net: Ipv6Network = "2001:db8::/96".parse().unwrap(); /// let net: Ipv6Network = "2001:db8::/96".parse().unwrap();
/// assert_eq!(net.network(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)); /// assert_eq!(net.network(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
/// ``` /// ```
pub fn network(&self) -> Ipv6Addr { pub const fn network(&self) -> Ipv6Addr {
let mask = u128::from(self.mask()); let mask = self.mask().to_bits();
let network = u128::from(self.addr) & mask; let network = self.addr.to_bits() & mask;
Ipv6Addr::from(network) Ipv6Addr::from_bits(network)
} }
/// Returns the broadcast address of this `Ipv6Network`. /// Returns the broadcast address of this `Ipv6Network`.
@ -240,10 +240,10 @@ impl Ipv6Network {
/// let net: Ipv6Network = "2001:db8::/96".parse().unwrap(); /// let net: Ipv6Network = "2001:db8::/96".parse().unwrap();
/// assert_eq!(net.broadcast(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xffff, 0xffff)); /// assert_eq!(net.broadcast(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xffff, 0xffff));
/// ``` /// ```
pub fn broadcast(&self) -> Ipv6Addr { pub const fn broadcast(&self) -> Ipv6Addr {
let mask = u128::from(self.mask()); let mask = self.mask().to_bits();
let broadcast = u128::from(self.addr) | !mask; let broadcast = self.addr.to_bits() | !mask;
Ipv6Addr::from(broadcast) Ipv6Addr::from_bits(broadcast)
} }
/// Checks if a given `Ipv6Addr` is in this `Ipv6Network` /// Checks if a given `Ipv6Addr` is in this `Ipv6Network`
@ -259,10 +259,10 @@ impl Ipv6Network {
/// assert!(!net.contains(Ipv6Addr::new(0xffff, 0, 0, 0, 0, 0, 0, 0x1))); /// assert!(!net.contains(Ipv6Addr::new(0xffff, 0, 0, 0, 0, 0, 0, 0x1)));
/// ``` /// ```
#[inline] #[inline]
pub fn contains(&self, ip: Ipv6Addr) -> bool { pub const fn contains(&self, ip: Ipv6Addr) -> bool {
let ip = u128::from(ip); let ip = ip.to_bits();
let net = u128::from(self.network()); let net = self.network().to_bits();
let mask = u128::from(self.mask()); let mask = self.mask().to_bits();
(ip & mask) == net (ip & mask) == net
} }
@ -372,6 +372,15 @@ impl Iterator for Ipv6NetworkIterator {
}; };
Some(next.into()) Some(next.into())
} }
fn size_hint(&self) -> (usize, Option<usize>) {
if let Some(n) = self.next {
let elms = (self.end - n + 1) as usize;
(elms, Some(elms))
} else {
(0, None)
}
}
} }
impl IntoIterator for &'_ Ipv6Network { impl IntoIterator for &'_ Ipv6Network {
@ -391,12 +400,25 @@ impl fmt::Display for Ipv6Network {
/// Converts a `Ipv6Addr` network mask into a prefix. /// Converts a `Ipv6Addr` network mask into a prefix.
/// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`. /// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`.
pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> { pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> {
match ipv6_mask_to_prefix_checked(mask) {
Some(prefix) => Ok(prefix),
None => Err(IpNetworkError::InvalidPrefix),
}
}
/// Converts a `Ipv6Addr` network mask into a prefix.
///
/// If the mask is invalid this will return `None`. This is useful in const contexts where
/// [`Option::unwrap`] may be called to trigger a compile-time error if the prefix is invalid.
pub const fn ipv6_mask_to_prefix_checked(mask: Ipv6Addr) -> Option<u8> {
let mask = mask.segments(); let mask = mask.segments();
let mut mask_iter = mask.iter();
// Count the number of set bits from the start of the address // Count the number of set bits from the start of the address
let mut prefix = 0; let mut prefix = 0;
for &segment in &mut mask_iter { let mut i = 0;
while i < mask.len() {
let segment = mask[i];
i += 1;
if segment == 0xffff { if segment == 0xffff {
prefix += IPV6_SEGMENT_BITS; prefix += IPV6_SEGMENT_BITS;
} else if segment == 0 { } else if segment == 0 {
@ -406,7 +428,7 @@ pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> {
let prefix_bits = (!segment).leading_zeros() as u8; let prefix_bits = (!segment).leading_zeros() as u8;
// Check that the remainder of the bits are all unset // Check that the remainder of the bits are all unset
if segment << prefix_bits != 0 { if segment << prefix_bits != 0 {
return Err(IpNetworkError::InvalidPrefix); return None;
} }
prefix += prefix_bits; prefix += prefix_bits;
break; break;
@ -414,13 +436,15 @@ pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> {
} }
// Now check all the remaining bits are unset // Now check all the remaining bits are unset
for &segment in mask_iter { while i < mask.len() {
let segment = mask[i];
i += 1;
if segment != 0 { if segment != 0 {
return Err(IpNetworkError::InvalidPrefix); return None;
} }
} }
Ok(prefix) Some(prefix)
} }
#[cfg(test)] #[cfg(test)]
@ -597,6 +621,18 @@ mod test {
assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2), iter.next().unwrap()); assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2), iter.next().unwrap());
} }
#[test]
fn iterator_v6_size_hint() {
let cidr: Ipv6Network = "2001:db8::/128".parse().unwrap();
let mut iter = cidr.iter();
assert_eq!((1, Some(1)), iter.size_hint());
assert_eq!(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
iter.next().unwrap()
);
assert_eq!((0, None), iter.size_hint());
}
#[test] #[test]
fn network_v6() { fn network_v6() {
let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap(); let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap();

View File

@ -8,7 +8,12 @@
unused_import_braces unused_import_braces
)] )]
use std::{convert::TryFrom, fmt, net::IpAddr, str::FromStr}; use std::{
convert::TryFrom,
fmt,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
str::FromStr,
};
mod error; mod error;
mod ipv4; mod ipv4;
@ -16,11 +21,11 @@ mod ipv6;
mod parse; mod parse;
mod size; mod size;
pub use crate::error::{NetworkSizeError, IpNetworkError}; pub use crate::error::{IpNetworkError, NetworkSizeError};
pub use crate::ipv4::Ipv4NetworkIterator; pub use crate::ipv4::Ipv4NetworkIterator;
pub use crate::ipv4::{ipv4_mask_to_prefix, Ipv4Network}; pub use crate::ipv4::{ipv4_mask_to_prefix, ipv4_mask_to_prefix_checked, Ipv4Network};
pub use crate::ipv6::Ipv6NetworkIterator; pub use crate::ipv6::Ipv6NetworkIterator;
pub use crate::ipv6::{ipv6_mask_to_prefix, Ipv6Network}; pub use crate::ipv6::{ipv6_mask_to_prefix, ipv6_mask_to_prefix_checked, Ipv6Network};
pub use crate::size::NetworkSize; pub use crate::size::NetworkSize;
/// Represents a generic network range. This type can have two variants: /// Represents a generic network range. This type can have two variants:
@ -141,7 +146,7 @@ impl IpNetwork {
} }
/// Returns the IP part of a given `IpNetwork` /// Returns the IP part of a given `IpNetwork`
pub fn ip(&self) -> IpAddr { pub const fn ip(&self) -> IpAddr {
match *self { match *self {
IpNetwork::V4(ref a) => IpAddr::V4(a.ip()), IpNetwork::V4(ref a) => IpAddr::V4(a.ip()),
IpNetwork::V6(ref a) => IpAddr::V6(a.ip()), IpNetwork::V6(ref a) => IpAddr::V6(a.ip()),
@ -160,7 +165,7 @@ impl IpNetwork {
/// assert_eq!(IpNetwork::V6("ff01::0".parse().unwrap()).prefix(), 128u8); /// assert_eq!(IpNetwork::V6("ff01::0".parse().unwrap()).prefix(), 128u8);
/// assert_eq!(IpNetwork::V6("ff01::0/32".parse().unwrap()).prefix(), 32u8); /// assert_eq!(IpNetwork::V6("ff01::0/32".parse().unwrap()).prefix(), 32u8);
/// ``` /// ```
pub fn prefix(&self) -> u8 { pub const fn prefix(&self) -> u8 {
match *self { match *self {
IpNetwork::V4(ref a) => a.prefix(), IpNetwork::V4(ref a) => a.prefix(),
IpNetwork::V6(ref a) => a.prefix(), IpNetwork::V6(ref a) => a.prefix(),
@ -181,7 +186,7 @@ impl IpNetwork {
/// let net: IpNetwork = "2001:db8::/96".parse().unwrap(); /// let net: IpNetwork = "2001:db8::/96".parse().unwrap();
/// assert_eq!(net.network(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)); /// assert_eq!(net.network(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
/// ``` /// ```
pub fn network(&self) -> IpAddr { pub const fn network(&self) -> IpAddr {
match *self { match *self {
IpNetwork::V4(ref a) => IpAddr::V4(a.network()), IpNetwork::V4(ref a) => IpAddr::V4(a.network()),
IpNetwork::V6(ref a) => IpAddr::V6(a.network()), IpNetwork::V6(ref a) => IpAddr::V6(a.network()),
@ -200,7 +205,7 @@ impl IpNetwork {
/// let net: Ipv4Network = "10.9.0.32/16".parse().unwrap(); /// let net: Ipv4Network = "10.9.0.32/16".parse().unwrap();
/// assert_eq!(net.broadcast(), Ipv4Addr::new(10, 9, 255, 255)); /// assert_eq!(net.broadcast(), Ipv4Addr::new(10, 9, 255, 255));
/// ``` /// ```
pub fn broadcast(&self) -> IpAddr { pub const fn broadcast(&self) -> IpAddr {
match *self { match *self {
IpNetwork::V4(ref a) => IpAddr::V4(a.broadcast()), IpNetwork::V4(ref a) => IpAddr::V4(a.broadcast()),
IpNetwork::V6(ref a) => IpAddr::V6(a.broadcast()), IpNetwork::V6(ref a) => IpAddr::V6(a.broadcast()),
@ -226,7 +231,7 @@ impl IpNetwork {
/// let v6_net: IpNetwork = "ff01::0/32".parse().unwrap(); /// let v6_net: IpNetwork = "ff01::0/32".parse().unwrap();
/// assert_eq!(v6_net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0)); /// assert_eq!(v6_net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0));
/// ``` /// ```
pub fn mask(&self) -> IpAddr { pub const fn mask(&self) -> IpAddr {
match *self { match *self {
IpNetwork::V4(ref a) => IpAddr::V4(a.mask()), IpNetwork::V4(ref a) => IpAddr::V4(a.mask()),
IpNetwork::V6(ref a) => IpAddr::V6(a.mask()), IpNetwork::V6(ref a) => IpAddr::V6(a.mask()),
@ -245,7 +250,7 @@ impl IpNetwork {
/// assert_eq!(v4.is_ipv4(), true); /// assert_eq!(v4.is_ipv4(), true);
/// assert_eq!(v4.is_ipv6(), false); /// assert_eq!(v4.is_ipv6(), false);
///``` ///```
pub fn is_ipv4(&self) -> bool { pub const fn is_ipv4(&self) -> bool {
match *self { match *self {
IpNetwork::V4(_) => true, IpNetwork::V4(_) => true,
IpNetwork::V6(_) => false, IpNetwork::V6(_) => false,
@ -264,7 +269,7 @@ impl IpNetwork {
/// assert_eq!(v6.is_ipv6(), true); /// assert_eq!(v6.is_ipv6(), true);
/// assert_eq!(v6.is_ipv4(), false); /// assert_eq!(v6.is_ipv4(), false);
///``` ///```
pub fn is_ipv6(&self) -> bool { pub const fn is_ipv6(&self) -> bool {
match *self { match *self {
IpNetwork::V4(_) => false, IpNetwork::V4(_) => false,
IpNetwork::V6(_) => true, IpNetwork::V6(_) => true,
@ -292,7 +297,7 @@ impl IpNetwork {
/// assert!(!net.contains(ip4)); /// assert!(!net.contains(ip4));
/// ``` /// ```
#[inline] #[inline]
pub fn contains(&self, ip: IpAddr) -> bool { pub const fn contains(&self, ip: IpAddr) -> bool {
match (*self, ip) { match (*self, ip) {
(IpNetwork::V4(net), IpAddr::V4(ip)) => net.contains(ip), (IpNetwork::V4(net), IpAddr::V4(ip)) => net.contains(ip),
(IpNetwork::V6(net), IpAddr::V6(ip)) => net.contains(ip), (IpNetwork::V6(net), IpAddr::V6(ip)) => net.contains(ip),
@ -377,11 +382,23 @@ impl From<Ipv6Network> for IpNetwork {
} }
} }
impl From<Ipv4Addr> for IpNetwork {
fn from(addr: Ipv4Addr) -> IpNetwork {
IpNetwork::V4(Ipv4Network::from(addr))
}
}
impl From<Ipv6Addr> for IpNetwork {
fn from(addr: Ipv6Addr) -> IpNetwork {
IpNetwork::V6(Ipv6Network::from(addr))
}
}
impl From<IpAddr> for IpNetwork { impl From<IpAddr> for IpNetwork {
fn from(addr: IpAddr) -> IpNetwork { fn from(addr: IpAddr) -> IpNetwork {
match addr { match addr {
IpAddr::V4(a) => IpNetwork::V4(Ipv4Network::from(a)), IpAddr::V4(a) => IpNetwork::from(a),
IpAddr::V6(a) => IpNetwork::V6(Ipv6Network::from(a)), IpAddr::V6(a) => IpNetwork::from(a),
} }
} }
} }
@ -414,6 +431,12 @@ impl Iterator for IpNetworkIterator {
IpNetworkIteratorInner::V6(iter) => iter.next().map(IpAddr::V6), IpNetworkIteratorInner::V6(iter) => iter.next().map(IpAddr::V6),
} }
} }
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.inner {
IpNetworkIteratorInner::V4(iter) => iter.size_hint(),
IpNetworkIteratorInner::V6(iter) => iter.size_hint(),
}
}
} }
impl IntoIterator for &'_ IpNetwork { impl IntoIterator for &'_ IpNetwork {
@ -433,6 +456,17 @@ pub fn ip_mask_to_prefix(mask: IpAddr) -> Result<u8, IpNetworkError> {
} }
} }
/// Converts a `IpAddr` network mask into a prefix.
///
/// If the mask is invalid this will return `None`. This is useful in const contexts where
/// [`Option::unwrap`] may be called to trigger a compile-time error if the prefix is invalid.
pub const fn ip_mask_to_prefix_checked(mask: IpAddr) -> Option<u8> {
match mask {
IpAddr::V4(mask) => ipv4_mask_to_prefix_checked(mask),
IpAddr::V6(mask) => ipv6_mask_to_prefix_checked(mask),
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
#[test] #[test]