Skip to main content

bare_types/net/
macaddress.rs

1//! MAC address type for network programming.
2//!
3//! This module provides a type-safe abstraction for MAC addresses,
4//! ensuring compliance with IEEE 802 MAC address specifications.
5//!
6//! # IEEE 802 MAC Address Rules
7//!
8//! According to [IEEE 802](https://standards.ieee.org/standards/802/):
9//!
10//! - Length: 6 bytes (48 bits) for standard MAC addresses
11//! - Format: XX:XX:XX:XX:XX:XX (colon-separated hex pairs)
12//! - Alternative formats: XX-XX-XX-XX-XX-XX (dash-separated)
13//! - Each octet: 00-FF (0-255 in decimal)
14//! - First octet's least significant bit indicates unicast (0) or multicast (1)
15//! - First octet's second least significant bit indicates globally (0) or locally (1) administered
16//!
17//! # Examples
18//!
19//! ```rust
20//! use bare_types::net::MacAddress;
21//!
22//! // Create a MAC address
23//! let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55])?;
24//!
25//! // Check if it's unicast
26//! assert!(mac.is_unicast());
27//!
28//! // Check if it's globally administered
29//! assert!(mac.is_globally_administered());
30//!
31//! // Get the string representation
32//! assert_eq!(mac.as_str(), "00:11:22:33:44:55");
33//!
34//! // Parse from string
35//! let mac: MacAddress = "00:11:22:33:44:55".parse()?;
36//! # Ok::<(), bare_types::net::MacAddressError>(())
37//! ```
38
39use core::fmt;
40use core::str::FromStr;
41
42#[cfg(feature = "serde")]
43use serde::{Deserialize, Serialize};
44
45#[cfg(feature = "zeroize")]
46use zeroize::Zeroize;
47
48/// Error type for MAC address validation.
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
51#[non_exhaustive]
52pub enum MacAddressError {
53    /// Invalid length
54    ///
55    /// MAC addresses must be exactly 6 bytes (48 bits).
56    /// This variant contains the actual length.
57    InvalidLength(usize),
58    /// Invalid format
59    ///
60    /// The MAC address string is not in a valid format.
61    /// Expected format: XX:XX:XX:XX:XX:XX or XX-XX-XX-XX-XX-XX
62    InvalidFormat,
63    /// Invalid octet
64    ///
65    /// One of the octets is invalid (not a valid hex value 00-FF).
66    InvalidOctet,
67}
68
69impl fmt::Display for MacAddressError {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        match self {
72            Self::InvalidLength(len) => {
73                write!(f, "MAC address must be 6 bytes (got {len})")
74            }
75            Self::InvalidFormat => {
76                write!(
77                    f,
78                    "MAC address must be in format XX:XX:XX:XX:XX:XX or XX-XX-XX-XX-XX-XX"
79                )
80            }
81            Self::InvalidOctet => {
82                write!(f, "MAC address octet is invalid (must be 00-FF)")
83            }
84        }
85    }
86}
87
88#[cfg(feature = "std")]
89impl std::error::Error for MacAddressError {}
90
91/// A MAC address.
92///
93/// This type provides type-safe MAC addresses with IEEE 802 validation.
94/// It uses the newtype pattern with `#[repr(transparent)]` for zero-cost abstraction.
95///
96/// # Invariants
97///
98/// - Exactly 6 bytes (48 bits)
99/// - Each octet is 0-255
100///
101/// # Examples
102///
103/// ```rust
104/// use bare_types::net::MacAddress;
105///
106/// // Create a MAC address
107/// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55])?;
108///
109/// // Access the string representation
110/// assert_eq!(mac.as_str(), "00:11:22:33:44:55");
111///
112/// // Check if it's unicast
113/// assert!(mac.is_unicast());
114///
115/// // Get the octets
116/// assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
117///
118/// // Parse from string
119/// let mac: MacAddress = "00:11:22:33:44:55".parse()?;
120/// # Ok::<(), bare_types::net::MacAddressError>(())
121/// ```
122#[repr(transparent)]
123#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
124#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
125#[cfg_attr(feature = "zeroize", derive(Zeroize))]
126pub struct MacAddress([u8; 6]);
127
128#[cfg(feature = "arbitrary")]
129impl<'a> arbitrary::Arbitrary<'a> for MacAddress {
130    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
131        let mut octets = [0u8; 6];
132        for octet in &mut octets {
133            *octet = u8::arbitrary(u)?;
134        }
135        Ok(Self(octets))
136    }
137}
138
139impl MacAddress {
140    /// Creates a new MAC address from an array of 6 bytes.
141    ///
142    /// # Errors
143    ///
144    /// Returns `MacAddressError` if the array is not exactly 6 bytes.
145    ///
146    /// # Examples
147    ///
148    /// ```rust
149    /// use bare_types::net::MacAddress;
150    ///
151    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55])?;
152    /// assert_eq!(mac.as_str(), "00:11:22:33:44:55");
153    /// # Ok::<(), bare_types::net::MacAddressError>(())
154    /// ```
155    #[inline]
156    pub const fn new(octets: [u8; 6]) -> Result<Self, MacAddressError> {
157        Ok(Self(octets))
158    }
159
160    /// Creates a new MAC address from a slice of bytes.
161    ///
162    /// # Errors
163    ///
164    /// Returns `MacAddressError` if the slice is not exactly 6 bytes.
165    ///
166    /// # Examples
167    ///
168    /// ```rust
169    /// use bare_types::net::MacAddress;
170    ///
171    /// let bytes = vec![0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
172    /// let mac = MacAddress::from_bytes(&bytes)?;
173    /// assert_eq!(mac.as_str(), "00:11:22:33:44:55");
174    /// # Ok::<(), bare_types::net::MacAddressError>(())
175    /// ```
176    pub fn from_bytes(bytes: &[u8]) -> Result<Self, MacAddressError> {
177        if bytes.len() != 6 {
178            return Err(MacAddressError::InvalidLength(bytes.len()));
179        }
180        let mut octets = [0u8; 6];
181        octets.copy_from_slice(bytes);
182        Ok(Self(octets))
183    }
184
185    /// Creates a new MAC address from a string.
186    ///
187    /// # Errors
188    ///
189    /// Returns `MacAddressError` if of string is not in a valid format.
190    ///
191    /// # Examples
192    ///
193    /// ```rust
194    /// use bare_types::net::MacAddress;
195    ///
196    /// let mac = MacAddress::from_str("00:11:22:33:44:55")?;
197    /// assert_eq!(mac.as_str(), "00:11:22:33:44:55");
198    /// # Ok::<(), bare_types::net::MacAddressError>(())
199    /// ```
200    #[allow(clippy::should_implement_trait)]
201    pub fn from_str(s: &str) -> Result<Self, MacAddressError> {
202        let parts: Vec<&str> = if s.contains(':') {
203            s.split(':').collect()
204        } else if s.contains('-') {
205            s.split('-').collect()
206        } else {
207            return Err(MacAddressError::InvalidFormat);
208        };
209
210        if parts.len() != 6 {
211            return Err(MacAddressError::InvalidFormat);
212        }
213
214        let mut octets = [0u8; 6];
215        for (i, part) in parts.iter().enumerate() {
216            let octet = u8::from_str_radix(part, 16).map_err(|_| MacAddressError::InvalidOctet)?;
217            octets[i] = octet;
218        }
219
220        Ok(Self(octets))
221    }
222
223    /// Returns the MAC address as a string slice in colon-separated format.
224    ///
225    /// # Examples
226    ///
227    /// ```rust
228    /// use bare_types::net::MacAddress;
229    ///
230    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
231    /// assert_eq!(mac.as_str(), "00:11:22:33:44:55");
232    /// ```
233    #[must_use]
234    pub fn as_str(&self) -> String {
235        format!(
236            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
237            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
238        )
239    }
240
241    /// Returns the MAC address as a string slice in dash-separated format.
242    ///
243    /// # Examples
244    ///
245    /// ```rust
246    /// use bare_types::net::MacAddress;
247    ///
248    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
249    /// assert_eq!(mac.as_str_dash(), "00-11-22-33-44-55");
250    /// ```
251    #[must_use]
252    pub fn as_str_dash(&self) -> String {
253        format!(
254            "{:02X}-{:02X}-{:02X}-{:02X}-{:02X}-{:02X}",
255            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
256        )
257    }
258
259    /// Returns the MAC address as a string slice in lowercase colon-separated format.
260    ///
261    /// # Examples
262    ///
263    /// ```rust
264    /// use bare_types::net::MacAddress;
265    ///
266    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
267    /// assert_eq!(mac.as_str_lower(), "00:11:22:33:44:55");
268    /// ```
269    #[must_use]
270    pub fn as_str_lower(&self) -> String {
271        format!(
272            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
273            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
274        )
275    }
276
277    /// Returns a reference to the underlying array of 6 bytes.
278    ///
279    /// # Examples
280    ///
281    /// ```rust
282    /// use bare_types::net::MacAddress;
283    ///
284    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
285    /// let octets: &[u8; 6] = mac.as_inner();
286    /// assert_eq!(octets, &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
287    /// ```
288    #[must_use]
289    #[inline]
290    pub const fn as_inner(&self) -> &[u8; 6] {
291        &self.0
292    }
293
294    /// Consumes this MAC address and returns the underlying array of 6 bytes.
295    ///
296    /// # Examples
297    ///
298    /// ```rust
299    /// use bare_types::net::MacAddress;
300    ///
301    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
302    /// let octets = mac.into_inner();
303    /// assert_eq!(octets, [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
304    /// ```
305    #[must_use]
306    #[inline]
307    pub const fn into_inner(self) -> [u8; 6] {
308        self.0
309    }
310
311    /// Returns the octets of the MAC address.
312    ///
313    /// # Examples
314    ///
315    /// ```rust
316    /// use bare_types::net::MacAddress;
317    ///
318    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
319    /// assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
320    /// ```
321    #[must_use]
322    #[inline]
323    pub const fn octets(&self) -> [u8; 6] {
324        self.0
325    }
326
327    /// Returns `true` if this is a unicast MAC address.
328    ///
329    /// The least significant bit of the first octet indicates unicast (0) or multicast (1).
330    ///
331    /// # Examples
332    ///
333    /// ```rust
334    /// use bare_types::net::MacAddress;
335    ///
336    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
337    /// assert!(mac.is_unicast());
338    /// ```
339    #[must_use]
340    #[inline]
341    pub const fn is_unicast(&self) -> bool {
342        self.0[0] & 0x01 == 0
343    }
344
345    /// Returns `true` if this is a multicast MAC address.
346    ///
347    /// The least significant bit of the first octet indicates unicast (0) or multicast (1).
348    ///
349    /// # Examples
350    ///
351    /// ```rust
352    /// use bare_types::net::MacAddress;
353    ///
354    /// let mac = MacAddress::new([0x01, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
355    /// assert!(mac.is_multicast());
356    /// ```
357    #[must_use]
358    #[inline]
359    pub const fn is_multicast(&self) -> bool {
360        self.0[0] & 0x01 == 1
361    }
362
363    /// Returns `true` if this is a globally administered MAC address.
364    ///
365    /// The second least significant bit of the first octet indicates globally (0) or locally (1) administered.
366    ///
367    /// # Examples
368    ///
369    /// ```rust
370    /// use bare_types::net::MacAddress;
371    ///
372    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
373    /// assert!(mac.is_globally_administered());
374    /// ```
375    #[must_use]
376    #[inline]
377    pub const fn is_globally_administered(&self) -> bool {
378        self.0[0] & 0x02 == 0
379    }
380
381    /// Returns `true` if this is a locally administered MAC address.
382    ///
383    /// The second least significant bit of the first octet indicates globally (0) or locally (1) administered.
384    ///
385    /// # Examples
386    ///
387    /// ```rust
388    /// use bare_types::net::MacAddress;
389    ///
390    /// let mac = MacAddress::new([0x02, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
391    /// assert!(mac.is_locally_administered());
392    /// ```
393    #[must_use]
394    #[inline]
395    pub const fn is_locally_administered(&self) -> bool {
396        self.0[0] & 0x02 == 2
397    }
398
399    /// Returns the OUI (Organizationally Unique Identifier) of the MAC address.
400    ///
401    /// The OUI is the first 3 octets of the MAC address.
402    ///
403    /// # Examples
404    ///
405    /// ```rust
406    /// use bare_types::net::MacAddress;
407    ///
408    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
409    /// assert_eq!(mac.oui(), [0x00, 0x11, 0x22]);
410    /// ```
411    #[must_use]
412    #[inline]
413    pub const fn oui(&self) -> [u8; 3] {
414        [self.0[0], self.0[1], self.0[2]]
415    }
416
417    /// Returns the NIC (Network Interface Controller) specific part of the MAC address.
418    ///
419    /// The NIC specific part is the last 3 octets of the MAC address.
420    ///
421    /// # Examples
422    ///
423    /// ```rust
424    /// use bare_types::net::MacAddress;
425    ///
426    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
427    /// assert_eq!(mac.nic(), [0x33, 0x44, 0x55]);
428    /// ```
429    #[must_use]
430    #[inline]
431    pub const fn nic(&self) -> [u8; 3] {
432        [self.0[3], self.0[4], self.0[5]]
433    }
434}
435
436impl TryFrom<&[u8]> for MacAddress {
437    type Error = MacAddressError;
438
439    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
440        Self::from_bytes(bytes)
441    }
442}
443
444impl TryFrom<&str> for MacAddress {
445    type Error = MacAddressError;
446
447    fn try_from(s: &str) -> Result<Self, Self::Error> {
448        Self::from_str(s)
449    }
450}
451
452impl From<MacAddress> for [u8; 6] {
453    fn from(mac: MacAddress) -> Self {
454        mac.0
455    }
456}
457
458impl From<[u8; 6]> for MacAddress {
459    fn from(octets: [u8; 6]) -> Self {
460        Self(octets)
461    }
462}
463
464impl FromStr for MacAddress {
465    type Err = MacAddressError;
466
467    fn from_str(s: &str) -> Result<Self, Self::Err> {
468        Self::from_str(s)
469    }
470}
471
472impl fmt::Display for MacAddress {
473    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474        write!(
475            f,
476            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
477            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
478        )
479    }
480}
481
482#[cfg(test)]
483mod tests {
484    use super::*;
485
486    #[test]
487    fn test_new_valid_mac() {
488        assert!(MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).is_ok());
489        assert!(MacAddress::new([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).is_ok());
490        assert!(MacAddress::new([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).is_ok());
491    }
492
493    #[test]
494    fn test_from_bytes_valid() {
495        let bytes = vec![0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
496        assert!(MacAddress::from_bytes(&bytes).is_ok());
497    }
498
499    #[test]
500    fn test_from_bytes_invalid_length() {
501        let bytes = vec![0x00, 0x11, 0x22];
502        assert_eq!(
503            MacAddress::from_bytes(&bytes),
504            Err(MacAddressError::InvalidLength(3))
505        );
506    }
507
508    #[test]
509    fn test_from_str_valid() {
510        assert!(MacAddress::from_str("00:11:22:33:44:55").is_ok());
511        assert!(MacAddress::from_str("00-11-22-33-44-55").is_ok());
512        assert!(MacAddress::from_str("FF:FF:FF:FF:FF:FF").is_ok());
513    }
514
515    #[test]
516    fn test_from_str_invalid_format() {
517        assert_eq!(
518            MacAddress::from_str("00:11:22:33:44"),
519            Err(MacAddressError::InvalidFormat)
520        );
521        assert_eq!(
522            MacAddress::from_str("00:11:22:33:44:55:66"),
523            Err(MacAddressError::InvalidFormat)
524        );
525    }
526
527    #[test]
528    fn test_from_str_invalid_octet() {
529        assert_eq!(
530            MacAddress::from_str("GG:11:22:33:44:55"),
531            Err(MacAddressError::InvalidOctet)
532        );
533    }
534
535    #[test]
536    fn test_as_str() {
537        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
538        assert_eq!(mac.as_str(), "00:11:22:33:44:55");
539    }
540
541    #[test]
542    fn test_as_str_dash() {
543        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
544        assert_eq!(mac.as_str_dash(), "00-11-22-33-44-55");
545    }
546
547    #[test]
548    fn test_as_str_lower() {
549        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
550        assert_eq!(mac.as_str_lower(), "00:11:22:33:44:55");
551    }
552
553    #[test]
554    fn test_as_inner() {
555        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
556        let octets = mac.as_inner();
557        assert_eq!(octets, &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
558    }
559
560    #[test]
561    fn test_into_inner() {
562        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
563        let octets = mac.into_inner();
564        assert_eq!(octets, [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
565    }
566
567    #[test]
568    fn test_octets() {
569        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
570        assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
571    }
572
573    #[test]
574    fn test_is_unicast() {
575        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
576        assert!(mac.is_unicast());
577        assert!(!mac.is_multicast());
578    }
579
580    #[test]
581    fn test_is_multicast() {
582        let mac = MacAddress::new([0x01, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
583        assert!(mac.is_multicast());
584        assert!(!mac.is_unicast());
585    }
586
587    #[test]
588    fn test_is_globally_administered() {
589        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
590        assert!(mac.is_globally_administered());
591        assert!(!mac.is_locally_administered());
592    }
593
594    #[test]
595    fn test_is_locally_administered() {
596        let mac = MacAddress::new([0x02, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
597        assert!(mac.is_locally_administered());
598        assert!(!mac.is_globally_administered());
599    }
600
601    #[test]
602    fn test_oui() {
603        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
604        assert_eq!(mac.oui(), [0x00, 0x11, 0x22]);
605    }
606
607    #[test]
608    fn test_nic() {
609        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
610        assert_eq!(mac.nic(), [0x33, 0x44, 0x55]);
611    }
612
613    #[test]
614    fn test_try_from_bytes() {
615        let bytes = vec![0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
616        let mac = MacAddress::try_from(bytes.as_slice()).unwrap();
617        assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
618    }
619
620    #[test]
621    fn test_try_from_str() {
622        let mac = MacAddress::try_from("00:11:22:33:44:55").unwrap();
623        assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
624    }
625
626    #[test]
627    fn test_from_mac_to_array() {
628        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
629        let octets: [u8; 6] = mac.into();
630        assert_eq!(octets, [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
631    }
632
633    #[test]
634    fn test_from_array_to_mac() {
635        let octets = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
636        let mac: MacAddress = octets.into();
637        assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
638    }
639
640    #[test]
641    fn test_from_str() {
642        let mac: MacAddress = "00:11:22:33:44:55".parse().unwrap();
643        assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
644    }
645
646    #[test]
647    fn test_from_str_invalid() {
648        assert!("00:11:22:33:44".parse::<MacAddress>().is_err());
649        assert!("GG:11:22:33:44:55".parse::<MacAddress>().is_err());
650    }
651
652    #[test]
653    fn test_display() {
654        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
655        assert_eq!(format!("{mac}"), "00:11:22:33:44:55");
656    }
657
658    #[test]
659    fn test_equality() {
660        let mac1 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
661        let mac2 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
662        let mac3 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x56]).unwrap();
663
664        assert_eq!(mac1, mac2);
665        assert_ne!(mac1, mac3);
666    }
667
668    #[test]
669    fn test_ordering() {
670        let mac1 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
671        let mac2 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x56]).unwrap();
672
673        assert!(mac1 < mac2);
674    }
675
676    #[test]
677    fn test_clone() {
678        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
679        let mac2 = mac.clone();
680        assert_eq!(mac, mac2);
681    }
682
683    #[test]
684    fn test_error_display() {
685        assert_eq!(
686            format!("{}", MacAddressError::InvalidLength(3)),
687            "MAC address must be 6 bytes (got 3)"
688        );
689        assert_eq!(
690            format!("{}", MacAddressError::InvalidFormat),
691            "MAC address must be in format XX:XX:XX:XX:XX:XX or XX-XX-XX-XX-XX-XX"
692        );
693        assert_eq!(
694            format!("{}", MacAddressError::InvalidOctet),
695            "MAC address octet is invalid (must be 00-FF)"
696        );
697    }
698
699    #[test]
700    fn test_broadcast_address() {
701        let mac = MacAddress::new([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).unwrap();
702        assert!(mac.is_multicast());
703    }
704
705    #[test]
706    fn test_hash() {
707        use core::hash::Hash;
708        use core::hash::Hasher;
709
710        #[derive(Default)]
711        struct SimpleHasher(u64);
712
713        impl Hasher for SimpleHasher {
714            fn finish(&self) -> u64 {
715                self.0
716            }
717
718            fn write(&mut self, bytes: &[u8]) {
719                for byte in bytes {
720                    self.0 = self.0.wrapping_mul(31).wrapping_add(*byte as u64);
721                }
722            }
723        }
724
725        let mac1 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
726        let mac2 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
727        let mac3 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x56]).unwrap();
728
729        let mut hasher1 = SimpleHasher::default();
730        let mut hasher2 = SimpleHasher::default();
731        let mut hasher3 = SimpleHasher::default();
732
733        mac1.hash(&mut hasher1);
734        mac2.hash(&mut hasher2);
735        mac3.hash(&mut hasher3);
736
737        assert_eq!(hasher1.finish(), hasher2.finish());
738        assert_ne!(hasher1.finish(), hasher3.finish());
739    }
740
741    #[test]
742    fn test_debug() {
743        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
744        assert_eq!(format!("{:?}", mac), "MacAddress([0, 17, 34, 51, 68, 85])");
745    }
746
747    #[test]
748    fn test_from_into_inner_roundtrip() {
749        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
750        let octets = mac.into_inner();
751        let mac2 = MacAddress::new(octets).unwrap();
752        assert_eq!(mac2.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
753    }
754}