etherparse/link/
macsec_an.rs

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