Repository: achanda/ipnetwork (language: rust)

Issue:
`NetworkSize` should implement `From<u32>`, `From<u128>`, `TryInto<u32>` and `Into<u128>`
I have a PR for this, tracking issue.


Discussion / hints:
What is a usecase for implementing those for `NetworkSize`?

Fix patch:
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<u128> for NetworkSize {
+    fn from(value: u128) -> Self {
+        V6(value)
+    }
+}
+
+impl From<u32> for NetworkSize {
+    fn from(value: u32) -> Self {
+        V4(value)
+    }
+}
+
+impl TryInto<u32> for NetworkSize {
+    type Error = NetworkSizeError;
+    fn try_into(self) -> Result<u32, Self::Error> {
+        match self {
+            V4(a) => Ok(a),
+            V6(_) => Err(NetworkSizeError::NetworkIsTooLarge),
+        }
+    }
+}
+
+impl Into<u128> 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<Ordering> {
+        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::<u128>::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<u32, _> = 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<u32, _> = 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());
+    }
+}


Test patch: