1 use crate::err::ValueTooBigError;
2 
3 /// 12 bit unsigned integer containing the "VLAN identifier" (present
4 /// in the [`crate::SingleVlanHeader`]).
5 #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
6 pub struct VlanId(u16);
7 
8 impl VlanId {
9     /// VlanId with value 0.
10     pub const ZERO: VlanId = VlanId(0);
11 
12     /// Maximum value of an VLAN id.
13     pub const MAX_U16: u16 = 0b0000_1111_1111_1111;
14 
15     /// Tries to create an [`VlanId`] and checks that the passed value
16     /// is smaller or equal than [`VlanId::MAX_U16`] (12 bit unsigned integer).
17     ///
18     /// In case the passed value is bigger then what can be represented in an 12 bit
19     /// integer an error is returned. Otherwise an `Ok` containing the [`VlanId`].
20     ///
21     /// ```
22     /// use etherparse::VlanId;
23     ///
24     /// let vlanid = VlanId::try_new(2).unwrap();
25     /// assert_eq!(vlanid.value(), 2);
26     ///
27     /// // if a number that can not be represented in an 12 bit integer
28     /// // gets passed in an error is returned
29     /// use etherparse::err::{ValueTooBigError, ValueType};
30     /// assert_eq!(
31     ///     VlanId::try_new(VlanId::MAX_U16 + 1),
32     ///     Err(ValueTooBigError{
33     ///         actual: VlanId::MAX_U16 + 1,
34     ///         max_allowed: VlanId::MAX_U16,
35     ///         value_type: ValueType::VlanId,
36     ///     })
37     /// );
38     /// ```
39     #[inline]
try_new(value: u16) -> Result<VlanId, ValueTooBigError<u16>>40     pub const fn try_new(value: u16) -> Result<VlanId, ValueTooBigError<u16>> {
41         use crate::err::ValueType;
42         if value <= VlanId::MAX_U16 {
43             Ok(VlanId(value))
44         } else {
45             Err(ValueTooBigError {
46                 actual: value,
47                 max_allowed: VlanId::MAX_U16,
48                 value_type: ValueType::VlanId,
49             })
50         }
51     }
52 
53     /// Creates an [`VlanId`] WITHOUT checking that the value
54     /// is smaller or equal than [`VlanId::MAX_U16`] (12 bit unsigned integer).
55     /// The caller must guarantee that `value <= VlanId::MAX_U16`.
56     ///
57     /// # Safety
58     ///
59     /// `value` must be smaller or equal than [`VlanId::MAX_U16`]
60     /// otherwise the behavior of functions or data structures relying
61     /// on this pre-requirement is undefined.
62     #[inline]
new_unchecked(value: u16) -> VlanId63     pub const unsafe fn new_unchecked(value: u16) -> VlanId {
64         debug_assert!(value <= VlanId::MAX_U16);
65         VlanId(value)
66     }
67 
68     /// Returns the underlying unsigned 12 bit value as an `u16` value.
69     #[inline]
value(self) -> u1670     pub const fn value(self) -> u16 {
71         self.0
72     }
73 }
74 
75 impl core::fmt::Display for VlanId {
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<VlanId> for u16 {
83     #[inline]
from(value: VlanId) -> Self84     fn from(value: VlanId) -> Self {
85         value.0
86     }
87 }
88 
89 impl TryFrom<u16> for VlanId {
90     type Error = ValueTooBigError<u16>;
91 
92     #[inline]
try_from(value: u16) -> Result<Self, Self::Error>93     fn try_from(value: u16) -> Result<Self, Self::Error> {
94         use crate::err::ValueType;
95         if value <= VlanId::MAX_U16 {
96             Ok(VlanId(value))
97         } else {
98             Err(Self::Error {
99                 actual: value,
100                 max_allowed: VlanId::MAX_U16,
101                 value_type: ValueType::VlanId,
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 = VlanId(2);
119             let b = a;
120             assert_eq!(a, b);
121             assert_eq!(a.clone(), a);
122         }
123 
124         // default
125         {
126             let actual: VlanId = Default::default();
127             assert_eq!(actual.value(), 0);
128         }
129 
130         // debug
131         {
132             let a = VlanId(2);
133             assert_eq!(format!("{:?}", a), format!("VlanId(2)"));
134         }
135 
136         // ord & partial ord
137         {
138             use core::cmp::Ordering;
139             let a = VlanId(2);
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                 VlanId(2).hash(&mut hasher);
151                 hasher.finish()
152             };
153             let b = {
154                 let mut hasher = DefaultHasher::new();
155                 VlanId(2).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..=0b0000_1111_1111_1111u16,
166             invalid_value in 0b0001_0000_0000_0000u16..=u16::MAX
167         ) {
168             use crate::err::{ValueType, ValueTooBigError};
169             assert_eq!(
170                 valid_value,
171                 VlanId::try_new(valid_value).unwrap().value()
172             );
173             assert_eq!(
174                 VlanId::try_new(invalid_value).unwrap_err(),
175                 ValueTooBigError{
176                     actual: invalid_value,
177                     max_allowed: 0b0000_1111_1111_1111,
178                     value_type:  ValueType::VlanId
179                 }
180             );
181         }
182     }
183 
184     proptest! {
185         #[test]
186         fn try_from(
187             valid_value in 0..=0b0000_1111_1111_1111u16,
188             invalid_value in 0b0001_0000_0000_0000u16..=u16::MAX
189         ) {
190             use crate::err::{ValueType, ValueTooBigError};
191             // try_into
192             {
193                 let actual: VlanId = valid_value.try_into().unwrap();
194                 assert_eq!(actual.value(), valid_value);
195 
196                 let err: Result<VlanId, ValueTooBigError<u16>> = invalid_value.try_into();
197                 assert_eq!(
198                     err.unwrap_err(),
199                     ValueTooBigError{
200                         actual: invalid_value,
201                         max_allowed: 0b0000_1111_1111_1111,
202                         value_type:  ValueType::VlanId
203                     }
204                 );
205             }
206             // try_from
207             {
208                 assert_eq!(
209                     VlanId::try_from(valid_value).unwrap().value(),
210                     valid_value
211                 );
212 
213                 assert_eq!(
214                     VlanId::try_from(invalid_value).unwrap_err(),
215                     ValueTooBigError{
216                         actual: invalid_value,
217                         max_allowed: 0b0000_1111_1111_1111,
218                         value_type:  ValueType::VlanId
219                     }
220                 );
221             }
222         }
223     }
224 
225     proptest! {
226         #[test]
227         fn new_unchecked(valid_value in 0..=0b0000_1111_1111_1111u16) {
228             assert_eq!(
229                 valid_value,
230                 unsafe {
231                     VlanId::new_unchecked(valid_value).value()
232                 }
233             );
234         }
235     }
236 
237     proptest! {
238         #[test]
239         fn fmt(valid_value in 0..=0b0000_1111_1111_1111u16) {
240             assert_eq!(format!("{}", VlanId(valid_value)), format!("{}", valid_value));
241         }
242     }
243 
244     proptest! {
245         #[test]
246         fn from(valid_value in 0..=0b0000_1111_1111_1111u16,) {
247             let vlanid = VlanId::try_new(valid_value).unwrap();
248             let actual: u16 = vlanid.into();
249             assert_eq!(actual, valid_value);
250         }
251     }
252 }
253