Skip to main content

ipnetwork/
size.rs

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