1 use crate::err::ValueTooBigError;
2 
3 /// 2 bit unsigned integer containing the "Explicit Congestion
4 /// Notification" (present in the [`crate::Ipv4Header`]).
5 #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
6 pub struct Ipv4Ecn(u8);
7 
8 impl Ipv4Ecn {
9     /// Ipv4Ecn with value 0.
10     pub const ZERO: Ipv4Ecn = Ipv4Ecn(0);
11 
12     /// Ipv4Ecn with value 0.
13     pub const ONE: Ipv4Ecn = Ipv4Ecn(1);
14 
15     /// Ipv4Ecn with value 0.
16     pub const TWO: Ipv4Ecn = Ipv4Ecn(2);
17 
18     /// Ipv4Ecn with value 0.
19     pub const TRHEE: Ipv4Ecn = Ipv4Ecn(3);
20 
21     /// Maximum value of an IPv4 header ECN.
22     pub const MAX_U8: u8 = 0b0000_0011;
23 
24     /// Tries to create an [`Ipv4Ecn`] and checks that the passed value
25     /// is smaller or equal than [`Ipv4Ecn::MAX_U8`] (2 bit unsigned integer).
26     ///
27     /// In case the passed value is bigger then what can be represented in an 2 bit
28     /// integer an error is returned. Otherwise an `Ok` containing the [`Ipv4Ecn`].
29     ///
30     /// ```
31     /// use etherparse::Ipv4Ecn;
32     ///
33     /// let ecn = Ipv4Ecn::try_new(2).unwrap();
34     /// assert_eq!(ecn.value(), 2);
35     ///
36     /// // if a number that can not be represented in an 2 bit integer
37     /// // gets passed in an error is returned
38     /// use etherparse::err::{ValueTooBigError, ValueType};
39     /// assert_eq!(
40     ///     Ipv4Ecn::try_new(Ipv4Ecn::MAX_U8 + 1),
41     ///     Err(ValueTooBigError{
42     ///         actual: Ipv4Ecn::MAX_U8 + 1,
43     ///         max_allowed: Ipv4Ecn::MAX_U8,
44     ///         value_type: ValueType::Ipv4Ecn,
45     ///     })
46     /// );
47     /// ```
48     #[inline]
try_new(value: u8) -> Result<Ipv4Ecn, ValueTooBigError<u8>>49     pub const fn try_new(value: u8) -> Result<Ipv4Ecn, ValueTooBigError<u8>> {
50         use crate::err::ValueType;
51         if value <= Ipv4Ecn::MAX_U8 {
52             Ok(Ipv4Ecn(value))
53         } else {
54             Err(ValueTooBigError {
55                 actual: value,
56                 max_allowed: Ipv4Ecn::MAX_U8,
57                 value_type: ValueType::Ipv4Ecn,
58             })
59         }
60     }
61 
62     /// Creates an [`Ipv4Ecn`] without checking that the value
63     /// is smaller or equal than [`Ipv4Ecn::MAX_U8`] (2 bit unsigned integer).
64     /// The caller must guarantee that `value <= Ipv4Ecn::MAX_U8`.
65     ///
66     /// # Safety
67     ///
68     /// `value` must be smaller or equal than [`Ipv4Ecn::MAX_U8`]
69     /// otherwise the behavior of functions or data structures relying
70     /// on this pre-requirement is undefined.
71     #[inline]
new_unchecked(value: u8) -> Ipv4Ecn72     pub const unsafe fn new_unchecked(value: u8) -> Ipv4Ecn {
73         debug_assert!(value <= Ipv4Ecn::MAX_U8);
74         Ipv4Ecn(value)
75     }
76 
77     /// Returns the underlying unsigned 2 bit value as an `u8` value.
78     #[inline]
value(self) -> u879     pub const fn value(self) -> u8 {
80         self.0
81     }
82 }
83 
84 impl core::fmt::Display for Ipv4Ecn {
85     #[inline]
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result86     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87         self.0.fmt(f)
88     }
89 }
90 
91 impl From<Ipv4Ecn> for u8 {
92     #[inline]
from(value: Ipv4Ecn) -> Self93     fn from(value: Ipv4Ecn) -> Self {
94         value.0
95     }
96 }
97 
98 impl TryFrom<u8> for Ipv4Ecn {
99     type Error = ValueTooBigError<u8>;
100 
101     #[inline]
try_from(value: u8) -> Result<Self, Self::Error>102     fn try_from(value: u8) -> Result<Self, Self::Error> {
103         use crate::err::ValueType;
104         if value <= Ipv4Ecn::MAX_U8 {
105             Ok(Ipv4Ecn(value))
106         } else {
107             Err(Self::Error {
108                 actual: value,
109                 max_allowed: Ipv4Ecn::MAX_U8,
110                 value_type: ValueType::Ipv4Ecn,
111             })
112         }
113     }
114 }
115 
116 #[cfg(test)]
117 mod test {
118     use super::*;
119     use core::hash::{Hash, Hasher};
120     use proptest::prelude::*;
121     use std::format;
122 
123     #[test]
derived_traits()124     fn derived_traits() {
125         // copy & clone
126         {
127             let a = Ipv4Ecn(2);
128             let b = a;
129             assert_eq!(a, b);
130             assert_eq!(a.clone(), a);
131         }
132 
133         // default
134         {
135             let actual: Ipv4Ecn = Default::default();
136             assert_eq!(actual.value(), 0);
137         }
138 
139         // debug
140         {
141             let a = Ipv4Ecn(2);
142             assert_eq!(format!("{:?}", a), format!("Ipv4Ecn(2)"));
143         }
144 
145         // ord & partial ord
146         {
147             use core::cmp::Ordering;
148             let a = Ipv4Ecn(2);
149             let b = a;
150             assert_eq!(a.cmp(&b), Ordering::Equal);
151             assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
152         }
153 
154         // hash
155         {
156             use std::collections::hash_map::DefaultHasher;
157             let a = {
158                 let mut hasher = DefaultHasher::new();
159                 Ipv4Ecn(2).hash(&mut hasher);
160                 hasher.finish()
161             };
162             let b = {
163                 let mut hasher = DefaultHasher::new();
164                 Ipv4Ecn(2).hash(&mut hasher);
165                 hasher.finish()
166             };
167             assert_eq!(a, b);
168         }
169     }
170 
171     proptest! {
172         #[test]
173         fn try_new(
174             valid_value in 0..=0b0000_0011u8,
175             invalid_value in 0b0000_0100u8..=u8::MAX
176         ) {
177             use crate::err::{ValueType, ValueTooBigError};
178             assert_eq!(
179                 valid_value,
180                 Ipv4Ecn::try_new(valid_value).unwrap().value()
181             );
182             assert_eq!(
183                 Ipv4Ecn::try_new(invalid_value).unwrap_err(),
184                 ValueTooBigError{
185                     actual: invalid_value,
186                     max_allowed: 0b0000_0011,
187                     value_type:  ValueType::Ipv4Ecn
188                 }
189             );
190         }
191     }
192 
193     proptest! {
194         #[test]
195         fn try_from(
196             valid_value in 0..=0b0000_0011u8,
197             invalid_value in 0b0000_0100u8..=u8::MAX
198         ) {
199             use crate::err::{ValueType, ValueTooBigError};
200             // try_into
201             {
202                 let actual: Ipv4Ecn = valid_value.try_into().unwrap();
203                 assert_eq!(actual.value(), valid_value);
204 
205                 let err: Result<Ipv4Ecn, ValueTooBigError<u8>> = invalid_value.try_into();
206                 assert_eq!(
207                     err.unwrap_err(),
208                     ValueTooBigError{
209                         actual: invalid_value,
210                         max_allowed: 0b0000_0011,
211                         value_type:  ValueType::Ipv4Ecn
212                     }
213                 );
214             }
215             // try_from
216             {
217                 assert_eq!(
218                     Ipv4Ecn::try_from(valid_value).unwrap().value(),
219                     valid_value
220                 );
221 
222                 assert_eq!(
223                     Ipv4Ecn::try_from(invalid_value).unwrap_err(),
224                     ValueTooBigError{
225                         actual: invalid_value,
226                         max_allowed: 0b0000_0011,
227                         value_type:  ValueType::Ipv4Ecn
228                     }
229                 );
230             }
231         }
232     }
233 
234     proptest! {
235         #[test]
236         fn new_unchecked(valid_value in 0..=0b0000_0011u8) {
237             assert_eq!(
238                 valid_value,
239                 unsafe {
240                     Ipv4Ecn::new_unchecked(valid_value).value()
241                 }
242             );
243         }
244     }
245 
246     proptest! {
247         #[test]
248         fn fmt(valid_value in 0..=0b0000_0011u8) {
249             assert_eq!(format!("{}", Ipv4Ecn(valid_value)), format!("{}", valid_value));
250         }
251     }
252 
253     proptest! {
254         #[test]
255         fn from(valid_value in 0..=0b0000_0011u8,) {
256             let ecn = Ipv4Ecn::try_new(valid_value).unwrap();
257             let actual: u8 = ecn.into();
258             assert_eq!(actual, valid_value);
259         }
260     }
261 }
262