ipnetwork/
size.rs

1use std::{
2    cmp::Ordering,
3    fmt::Display,
4    hash::{Hash, Hasher},
5};
6
7use crate::error::NetworkSizeError;
8use NetworkSize::*;
9
10/// Represents a generic network size.
11///
12/// IPv4 network sizes are represented as `u32` values, while IPv6 network sizes are represented as `u128` values.
13///
14/// # Comparisons
15///
16/// Network sizes are compared by _value_, not by type.
17///
18/// ```
19/// use ipnetwork::NetworkSize;
20///
21/// let ns1 = NetworkSize::V4(100);
22/// let ns2 = NetworkSize::V6(100);
23///
24/// assert_eq!(ns1, ns2);
25/// ```
26#[derive(Debug, Clone, Copy)]
27pub enum NetworkSize {
28    V4(u32),
29    V6(u128),
30}
31
32impl NetworkSize {
33    /// Returns the size of the network as a `u128`
34    fn as_u128(&self) -> u128 {
35        match *self {
36            V4(a) => a as u128,
37            V6(a) => a,
38        }
39    }
40}
41
42impl From<u32> for NetworkSize {
43    fn from(value: u32) -> Self {
44        V4(value)
45    }
46}
47
48impl From<u128> for NetworkSize {
49    fn from(value: u128) -> Self {
50        V6(value)
51    }
52}
53
54impl TryFrom<NetworkSize> for u32 {
55    type Error = NetworkSizeError;
56    fn try_from(value: NetworkSize) -> Result<Self, Self::Error> {
57        match value {
58            V4(a) => Ok(a),
59            V6(_) => Err(NetworkSizeError::NetworkIsTooLarge),
60        }
61    }
62}
63
64impl From<NetworkSize> for u128 {
65    fn from(val: NetworkSize) -> Self {
66        val.as_u128()
67    }
68}
69
70impl PartialEq for NetworkSize {
71    fn eq(&self, other: &Self) -> bool {
72        let a = self.as_u128();
73        let b = other.as_u128();
74        a == b
75    }
76}
77
78impl Eq for NetworkSize {}
79
80impl Hash for NetworkSize {
81    fn hash<H: Hasher>(&self, state: &mut H) {
82        let a = self.as_u128();
83        a.hash(state);
84    }
85}
86
87impl Ord for NetworkSize {
88    fn cmp(&self, other: &Self) -> Ordering {
89        let a = self.as_u128();
90        let b = other.as_u128();
91        a.cmp(&b)
92    }
93}
94
95impl PartialOrd for NetworkSize {
96    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
97        Some(self.cmp(other))
98    }
99}
100
101impl Display for NetworkSize {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        write!(f, "{}", self.as_u128())
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn test_from_u128() {
113        let value: u128 = 100;
114        let ns = NetworkSize::from(value);
115        assert_eq!(ns, V6(100));
116    }
117
118    #[test]
119    fn test_from_u32() {
120        let value: u32 = 100;
121        let ns = NetworkSize::from(value);
122        assert_eq!(ns, V4(100));
123    }
124
125    #[test]
126    fn test_try_into_u32() {
127        let value: u32 = 100;
128        let ns = V4(value);
129        let result: Result<u32, _> = ns.try_into();
130        assert!(result.is_ok());
131        assert_eq!(result.unwrap(), value);
132    }
133
134    #[test]
135    fn test_try_into_u32_error() {
136        let value: u128 = u32::MAX as u128 + 1;
137        let ns = V6(value);
138        let result: Result<u32, _> = ns.try_into();
139        assert!(result.is_err());
140    }
141
142    #[test]
143    fn test_into_u128() {
144        let value: u32 = 100;
145        let ns = V4(value);
146        let result: u128 = ns.into();
147        assert_eq!(result, value as u128);
148    }
149
150    #[test]
151    fn test_eq() {
152        let ns1 = V4(100);
153        let ns2 = V4(100);
154        assert_eq!(ns1, ns2);
155
156        let ns1 = V6(100);
157        let ns2 = V6(100);
158        assert_eq!(ns1, ns2);
159
160        let ns1 = V4(100);
161        let ns2 = V6(100);
162        assert_eq!(ns1, ns2);
163    }
164
165    #[test]
166    fn test_cmp() {
167        let ns1 = V4(100);
168        let ns2 = V4(200);
169        assert!(ns1 < ns2);
170
171        let ns1 = V6(200);
172        let ns2 = V6(100);
173        assert!(ns1 > ns2);
174
175        let ns1 = V4(100);
176        let ns2 = V6(200);
177        assert!(ns1 < ns2);
178    }
179
180    #[test]
181    fn test_display() {
182        let ns1 = V4(u32::MAX);
183        let ns2 = V6(ns1.into());
184        assert_eq!(ns1.to_string(), ns2.to_string());
185    }
186
187    // Verify that [`std::hash::Hash`] and [`std::cmp::PartialEq`] are consistent
188    #[test]
189    fn test_hash() {
190        let a = NetworkSize::V4(100);
191        let b = NetworkSize::V6(100);
192
193        // Calculate the hash of the two values
194        let mut hasher = std::hash::DefaultHasher::default();
195        a.hash(&mut hasher);
196        let hash_a = hasher.finish();
197
198        let mut hasher = std::hash::DefaultHasher::default();
199        b.hash(&mut hasher);
200        let hash_b = hasher.finish();
201
202        // a == b
203        assert_eq!(a, b);
204        // implies hash(a) == hash(b)
205        assert_eq!(hash_a, hash_b);
206    }
207}