diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..e58781b --- /dev/null +++ b/src/error.rs @@ -0,0 +1,50 @@ +use std::{error::Error, fmt}; + +use crate::error::IpNetworkError::*; + +/// Represents a bunch of errors that can occur while working with a `IpNetwork` +#[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum IpNetworkError { + InvalidAddr(String), + InvalidPrefix, + InvalidCidrFormat(String), + NetworkSizeError(NetworkSizeError) +} + +impl fmt::Display for IpNetworkError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + InvalidAddr(ref s) => write!(f, "invalid address: {s}"), + InvalidPrefix => write!(f, "invalid prefix"), + InvalidCidrFormat(ref s) => write!(f, "invalid cidr format: {s}"), + NetworkSizeError(ref e) => write!(f, "network size error: {e}") + } + } +} + +impl Error for IpNetworkError { + fn description(&self) -> &str { + match *self { + InvalidAddr(_) => "address is invalid", + InvalidPrefix => "prefix is invalid", + InvalidCidrFormat(_) => "cidr is invalid", + NetworkSizeError(_) => "network size error" + } + } +} + +/// Cannot convert an IPv6 network size to a u32 as it is a 128-bit value. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum NetworkSizeError { + NetworkIsTooLarge +} + +impl fmt::Display for NetworkSizeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Network is too large to fit into an unsigned 32-bit integer!") + } +} + +impl Error for NetworkSizeError {} diff --git a/src/ipv4.rs b/src/ipv4.rs index add244d..7627894 100644 --- a/src/ipv4.rs +++ b/src/ipv4.rs @@ -1,4 +1,5 @@ -use crate::common::{cidr_parts, parse_prefix, IpNetworkError}; +use crate::error::IpNetworkError; +use crate::parse::{cidr_parts, parse_prefix}; use std::{convert::TryFrom, fmt, net::Ipv4Addr, str::FromStr}; const IPV4_BITS: u8 = 32; diff --git a/src/ipv6.rs b/src/ipv6.rs index 243b7dd..599ec97 100644 --- a/src/ipv6.rs +++ b/src/ipv6.rs @@ -1,4 +1,5 @@ -use crate::common::{cidr_parts, parse_prefix, IpNetworkError}; +use crate::error::IpNetworkError; +use crate::parse::{cidr_parts, parse_prefix}; use std::{cmp, convert::TryFrom, fmt, net::Ipv6Addr, str::FromStr}; const IPV6_BITS: u8 = 128; diff --git a/src/lib.rs b/src/lib.rs index 8d3e353..4931c0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,15 +10,18 @@ use std::{convert::TryFrom, fmt, net::IpAddr, str::FromStr}; -mod common; +mod error; mod ipv4; mod ipv6; +mod parse; +mod size; -pub use crate::common::IpNetworkError; +pub use crate::error::{NetworkSizeError, IpNetworkError}; pub use crate::ipv4::Ipv4NetworkIterator; pub use crate::ipv4::{ipv4_mask_to_prefix, Ipv4Network}; pub use crate::ipv6::Ipv6NetworkIterator; pub use crate::ipv6::{ipv6_mask_to_prefix, Ipv6Network}; +pub use crate::size::NetworkSize; /// Represents a generic network range. This type can have two variants: /// the v4 and the v6 case. @@ -117,13 +120,6 @@ impl schemars::JsonSchema for IpNetwork { } } -/// Represents a generic network size. For IPv4, the max size is a u32 and for IPv6, it is a u128 -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum NetworkSize { - V4(u32), - V6(u128), -} - impl IpNetwork { /// Constructs a new `IpNetwork` from a given `IpAddr` and a prefix denoting the /// network size. If the prefix is larger than 32 (for IPv4) or 128 (for IPv6), this diff --git a/src/common.rs b/src/parse.rs similarity index 51% rename from src/common.rs rename to src/parse.rs index 4b65554..a70ae2f 100644 --- a/src/common.rs +++ b/src/parse.rs @@ -1,34 +1,4 @@ -use std::{error::Error, fmt}; - -/// Represents a bunch of errors that can occur while working with a `IpNetwork` -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum IpNetworkError { - InvalidAddr(String), - InvalidPrefix, - InvalidCidrFormat(String), -} - -impl fmt::Display for IpNetworkError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use crate::IpNetworkError::*; - match *self { - InvalidAddr(ref s) => write!(f, "invalid address: {s}"), - InvalidPrefix => write!(f, "invalid prefix"), - InvalidCidrFormat(ref s) => write!(f, "invalid cidr format: {s}"), - } - } -} - -impl Error for IpNetworkError { - fn description(&self) -> &str { - use crate::IpNetworkError::*; - match *self { - InvalidAddr(_) => "address is invalid", - InvalidPrefix => "prefix is invalid", - InvalidCidrFormat(_) => "cidr is invalid", - } - } -} +use crate::error::IpNetworkError; pub fn cidr_parts(cidr: &str) -> Result<(&str, Option<&str>), IpNetworkError> { // Try to find a single slash diff --git a/src/size.rs b/src/size.rs new file mode 100644 index 0000000..c03f788 --- /dev/null +++ b/src/size.rs @@ -0,0 +1,161 @@ +use std::{cmp::Ordering, fmt::Display}; + +use crate::error::NetworkSizeError; + +/// Represents a generic network size. For IPv4, the max size is a u32 and for IPv6, it is a u128 +#[derive(Debug, Clone, Copy, Hash)] +pub enum NetworkSize { + V4(u32), + V6(u128), +} +use NetworkSize::*; + +// Conversions + +impl From for NetworkSize { + fn from(value: u128) -> Self { + V6(value) + } +} + +impl From for NetworkSize { + fn from(value: u32) -> Self { + V4(value) + } +} + +impl TryInto for NetworkSize { + type Error = NetworkSizeError; + fn try_into(self) -> Result { + match self { + V4(a) => Ok(a), + V6(_) => Err(NetworkSizeError::NetworkIsTooLarge), + } + } +} + +impl Into for NetworkSize { + fn into(self) -> u128 { + match self { + V4(a) => a as u128, + V6(a) => a, + } + } +} + +// Equality/comparisons + +impl PartialEq for NetworkSize { + fn eq(&self, other: &Self) -> bool { + let a: u128 = (*self).into(); + let b: u128 = (*other).into(); + a == b + } +} + +impl Ord for NetworkSize { + fn cmp(&self, other: &Self) -> Ordering { + let a: u128 = (*self).into(); + let b: u128 = (*other).into(); + return a.cmp(&b); + } +} + +impl PartialOrd for NetworkSize { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Eq for NetworkSize {} + +// Display + +impl Display for NetworkSize { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", Into::::into(*self)) + } +} + +// Tests + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_from_u128() { + let value: u128 = 100; + let ns = NetworkSize::from(value); + assert_eq!(ns, V6(100)); + } + + #[test] + fn test_from_u32() { + let value: u32 = 100; + let ns = NetworkSize::from(value); + assert_eq!(ns, V4(100)); + } + + #[test] + fn test_try_into_u32() { + let value: u32 = 100; + let ns = V4(value); + let result: Result = ns.try_into(); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), value); + } + + #[test] + fn test_try_into_u32_error() { + let value: u128 = u32::MAX as u128 + 1; + let ns = V6(value); + let result: Result = ns.try_into(); + assert!(result.is_err()); + } + + #[test] + fn test_into_u128() { + let value: u32 = 100; + let ns = V4(value); + let result: u128 = ns.into(); + assert_eq!(result, value as u128); + } + + #[test] + fn test_eq() { + let ns1 = V4(100); + let ns2 = V4(100); + assert_eq!(ns1, ns2); + + let ns1 = V6(100); + let ns2 = V6(100); + assert_eq!(ns1, ns2); + + let ns1 = V4(100); + let ns2 = V6(100); + assert_eq!(ns1, ns2); + } + + #[test] + fn test_cmp() { + let ns1 = V4(100); + let ns2 = V4(200); + assert!(ns1 < ns2); + + let ns1 = V6(200); + let ns2 = V6(100); + assert!(ns1 > ns2); + + let ns1 = V4(100); + let ns2 = V6(200); + assert!(ns1 < ns2); + } + + #[test] + fn test_display() { + let ns1 = V4(u32::MAX); + let ns2 = V6(ns1.into()); + assert_eq!(ns1.to_string(), ns2.to_string()); + } +}