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