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