netsize: Hash implementation to match PartialEq (#186)

* netsize: hash implementation to match PartialEq

* netsize: Prefer `From` over `Into`, add documentation, and add Hash and PartialEq tests
This commit is contained in:
Christopher Mahoney
2024-04-29 23:23:28 -04:00
committed by GitHub
parent 6e32dd2e88
commit 241b1dcdf8

View File

@ -1,20 +1,41 @@
use std::{cmp::Ordering, fmt::Display}; use std::{
cmp::Ordering,
fmt::Display,
hash::{Hash, Hasher},
};
use crate::error::NetworkSizeError; use crate::error::NetworkSizeError;
use NetworkSize::*;
/// Represents a generic network size. For IPv4, the max size is a u32 and for IPv6, it is a u128 /// Represents a generic network size.
#[derive(Debug, Clone, Copy, Hash)] ///
/// IPv4 network sizes are represented as `u32` values, while IPv6 network sizes are represented as `u128` values.
///
/// # Comparisons
///
/// Network sizes are compared by _value_, not by type.
///
/// ```
/// use ipnetwork::NetworkSize;
///
/// let ns1 = NetworkSize::V4(100);
/// let ns2 = NetworkSize::V6(100);
///
/// assert_eq!(ns1, ns2);
/// ```
#[derive(Debug, Clone, Copy)]
pub enum NetworkSize { pub enum NetworkSize {
V4(u32), V4(u32),
V6(u128), V6(u128),
} }
use NetworkSize::*;
// Conversions impl NetworkSize {
/// Returns the size of the network as a `u128`
impl From<u128> for NetworkSize { fn as_u128(&self) -> u128 {
fn from(value: u128) -> Self { match *self {
V6(value) V4(a) => a as u128,
V6(a) => a,
}
} }
} }
@ -24,40 +45,50 @@ impl From<u32> for NetworkSize {
} }
} }
impl TryInto<u32> for NetworkSize { impl From<u128> for NetworkSize {
fn from(value: u128) -> Self {
V6(value)
}
}
impl TryFrom<NetworkSize> for u32 {
type Error = NetworkSizeError; type Error = NetworkSizeError;
fn try_into(self) -> Result<u32, Self::Error> { fn try_from(value: NetworkSize) -> Result<Self, Self::Error> {
match self { match value {
V4(a) => Ok(a), V4(a) => Ok(a),
V6(_) => Err(NetworkSizeError::NetworkIsTooLarge), V6(_) => Err(NetworkSizeError::NetworkIsTooLarge),
} }
} }
} }
impl Into<u128> for NetworkSize { impl From<NetworkSize> for u128 {
fn into(self) -> u128 { fn from(val: NetworkSize) -> Self {
match self { val.as_u128()
V4(a) => a as u128,
V6(a) => a,
}
} }
} }
// Equality/comparisons
impl PartialEq for NetworkSize { impl PartialEq for NetworkSize {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
let a: u128 = (*self).into(); let a = self.as_u128();
let b: u128 = (*other).into(); let b = other.as_u128();
a == b a == b
} }
} }
impl Eq for NetworkSize {}
impl Hash for NetworkSize {
fn hash<H: Hasher>(&self, state: &mut H) {
let a = self.as_u128();
a.hash(state);
}
}
impl Ord for NetworkSize { impl Ord for NetworkSize {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
let a: u128 = (*self).into(); let a = self.as_u128();
let b: u128 = (*other).into(); let b = other.as_u128();
return a.cmp(&b); a.cmp(&b)
} }
} }
@ -67,18 +98,12 @@ impl PartialOrd for NetworkSize {
} }
} }
impl Eq for NetworkSize {}
// Display
impl Display for NetworkSize { impl Display for NetworkSize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", Into::<u128>::into(*self)) write!(f, "{}", self.as_u128())
} }
} }
// Tests
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -158,4 +183,25 @@ mod tests {
let ns2 = V6(ns1.into()); let ns2 = V6(ns1.into());
assert_eq!(ns1.to_string(), ns2.to_string()); assert_eq!(ns1.to_string(), ns2.to_string());
} }
// Verify that [`std::hash::Hash`] and [`std::cmp::PartialEq`] are consistent
#[test]
fn test_hash() {
let a = NetworkSize::V4(100);
let b = NetworkSize::V6(100);
// Calculate the hash of the two values
let mut hasher = std::hash::DefaultHasher::default();
a.hash(&mut hasher);
let hash_a = hasher.finish();
let mut hasher = std::hash::DefaultHasher::default();
b.hash(&mut hasher);
let hash_b = hasher.finish();
// a == b
assert_eq!(a, b);
// implies hash(a) == hash(b)
assert_eq!(hash_a, hash_b);
}
} }