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    pub const fn new(octets: [u8; 6]) -> Result<Self, MacAddressError> {
156        Ok(Self(octets))
157    }
158
159    /// Creates a new MAC address from a slice of bytes.
160    ///
161    /// # Errors
162    ///
163    /// Returns `MacAddressError` if the slice is not exactly 6 bytes.
164    ///
165    /// # Examples
166    ///
167    /// ```rust
168    /// use bare_types::net::MacAddress;
169    ///
170    /// let bytes = vec![0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
171    /// let mac = MacAddress::from_bytes(&bytes)?;
172    /// assert_eq!(mac.as_str(), "00:11:22:33:44:55");
173    /// # Ok::<(), bare_types::net::MacAddressError>(())
174    /// ```
175    pub fn from_bytes(bytes: &[u8]) -> Result<Self, MacAddressError> {
176        if bytes.len() != 6 {
177            return Err(MacAddressError::InvalidLength(bytes.len()));
178        }
179        let mut octets = [0u8; 6];
180        octets.copy_from_slice(bytes);
181        Ok(Self(octets))
182    }
183
184    /// Creates a new MAC address from a string.
185    ///
186    /// # Errors
187    ///
188    /// Returns `MacAddressError` if of string is not in a valid format.
189    ///
190    /// # Examples
191    ///
192    /// ```rust
193    /// use bare_types::net::MacAddress;
194    ///
195    /// let mac = MacAddress::from_str("00:11:22:33:44:55")?;
196    /// assert_eq!(mac.as_str(), "00:11:22:33:44:55");
197    /// # Ok::<(), bare_types::net::MacAddressError>(())
198    /// ```
199    #[allow(clippy::should_implement_trait)]
200    pub fn from_str(s: &str) -> Result<Self, MacAddressError> {
201        let parts: Vec<&str> = if s.contains(':') {
202            s.split(':').collect()
203        } else if s.contains('-') {
204            s.split('-').collect()
205        } else {
206            return Err(MacAddressError::InvalidFormat);
207        };
208
209        if parts.len() != 6 {
210            return Err(MacAddressError::InvalidFormat);
211        }
212
213        let mut octets = [0u8; 6];
214        for (i, part) in parts.iter().enumerate() {
215            let octet = u8::from_str_radix(part, 16).map_err(|_| MacAddressError::InvalidOctet)?;
216            octets[i] = octet;
217        }
218
219        Ok(Self(octets))
220    }
221
222    /// Returns the MAC address as a string slice in colon-separated format.
223    ///
224    /// # Examples
225    ///
226    /// ```rust
227    /// use bare_types::net::MacAddress;
228    ///
229    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
230    /// assert_eq!(mac.as_str(), "00:11:22:33:44:55");
231    /// ```
232    #[must_use]
233    pub fn as_str(&self) -> String {
234        format!(
235            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
236            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
237        )
238    }
239
240    /// Returns the MAC address as a string slice in dash-separated format.
241    ///
242    /// # Examples
243    ///
244    /// ```rust
245    /// use bare_types::net::MacAddress;
246    ///
247    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
248    /// assert_eq!(mac.as_str_dash(), "00-11-22-33-44-55");
249    /// ```
250    #[must_use]
251    pub fn as_str_dash(&self) -> String {
252        format!(
253            "{:02X}-{:02X}-{:02X}-{:02X}-{:02X}-{:02X}",
254            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
255        )
256    }
257
258    /// Returns the MAC address as a string slice in lowercase colon-separated format.
259    ///
260    /// # Examples
261    ///
262    /// ```rust
263    /// use bare_types::net::MacAddress;
264    ///
265    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
266    /// assert_eq!(mac.as_str_lower(), "00:11:22:33:44:55");
267    /// ```
268    #[must_use]
269    pub fn as_str_lower(&self) -> String {
270        format!(
271            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
272            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
273        )
274    }
275
276    /// Returns a reference to the underlying array of 6 bytes.
277    ///
278    /// # Examples
279    ///
280    /// ```rust
281    /// use bare_types::net::MacAddress;
282    ///
283    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
284    /// let octets: &[u8; 6] = mac.as_inner();
285    /// assert_eq!(octets, &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
286    /// ```
287    #[must_use]
288    #[inline]
289    pub const fn as_inner(&self) -> &[u8; 6] {
290        &self.0
291    }
292
293    /// Consumes this MAC address and returns the underlying array of 6 bytes.
294    ///
295    /// # Examples
296    ///
297    /// ```rust
298    /// use bare_types::net::MacAddress;
299    ///
300    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
301    /// let octets = mac.into_inner();
302    /// assert_eq!(octets, [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
303    /// ```
304    #[must_use]
305    #[inline]
306    pub const fn into_inner(self) -> [u8; 6] {
307        self.0
308    }
309
310    /// Returns the octets of the MAC address.
311    ///
312    /// # Examples
313    ///
314    /// ```rust
315    /// use bare_types::net::MacAddress;
316    ///
317    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
318    /// assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
319    /// ```
320    #[must_use]
321    #[inline]
322    pub const fn octets(&self) -> [u8; 6] {
323        self.0
324    }
325
326    /// Returns `true` if this is a unicast MAC address.
327    ///
328    /// The least significant bit of the first octet indicates unicast (0) or multicast (1).
329    ///
330    /// # Examples
331    ///
332    /// ```rust
333    /// use bare_types::net::MacAddress;
334    ///
335    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
336    /// assert!(mac.is_unicast());
337    /// ```
338    #[must_use]
339    #[inline]
340    pub const fn is_unicast(&self) -> bool {
341        self.0[0] & 0x01 == 0
342    }
343
344    /// Returns `true` if this is a multicast MAC address.
345    ///
346    /// The least significant bit of the first octet indicates unicast (0) or multicast (1).
347    ///
348    /// # Examples
349    ///
350    /// ```rust
351    /// use bare_types::net::MacAddress;
352    ///
353    /// let mac = MacAddress::new([0x01, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
354    /// assert!(mac.is_multicast());
355    /// ```
356    #[must_use]
357    #[inline]
358    pub const fn is_multicast(&self) -> bool {
359        self.0[0] & 0x01 == 1
360    }
361
362    /// Returns `true` if this is a globally administered MAC address.
363    ///
364    /// The second least significant bit of the first octet indicates globally (0) or locally (1) administered.
365    ///
366    /// # Examples
367    ///
368    /// ```rust
369    /// use bare_types::net::MacAddress;
370    ///
371    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
372    /// assert!(mac.is_globally_administered());
373    /// ```
374    #[must_use]
375    #[inline]
376    pub const fn is_globally_administered(&self) -> bool {
377        self.0[0] & 0x02 == 0
378    }
379
380    /// Returns `true` if this is a locally administered MAC address.
381    ///
382    /// The second least significant bit of the first octet indicates globally (0) or locally (1) administered.
383    ///
384    /// # Examples
385    ///
386    /// ```rust
387    /// use bare_types::net::MacAddress;
388    ///
389    /// let mac = MacAddress::new([0x02, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
390    /// assert!(mac.is_locally_administered());
391    /// ```
392    #[must_use]
393    #[inline]
394    pub const fn is_locally_administered(&self) -> bool {
395        self.0[0] & 0x02 == 2
396    }
397
398    /// Returns the OUI (Organizationally Unique Identifier) of the MAC address.
399    ///
400    /// The OUI is the first 3 octets of the MAC address.
401    ///
402    /// # Examples
403    ///
404    /// ```rust
405    /// use bare_types::net::MacAddress;
406    ///
407    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
408    /// assert_eq!(mac.oui(), [0x00, 0x11, 0x22]);
409    /// ```
410    #[must_use]
411    #[inline]
412    pub const fn oui(&self) -> [u8; 3] {
413        [self.0[0], self.0[1], self.0[2]]
414    }
415
416    /// Returns the NIC (Network Interface Controller) specific part of the MAC address.
417    ///
418    /// The NIC specific part is the last 3 octets of the MAC address.
419    ///
420    /// # Examples
421    ///
422    /// ```rust
423    /// use bare_types::net::MacAddress;
424    ///
425    /// let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
426    /// assert_eq!(mac.nic(), [0x33, 0x44, 0x55]);
427    /// ```
428    #[must_use]
429    #[inline]
430    pub const fn nic(&self) -> [u8; 3] {
431        [self.0[3], self.0[4], self.0[5]]
432    }
433}
434
435impl TryFrom<&[u8]> for MacAddress {
436    type Error = MacAddressError;
437
438    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
439        Self::from_bytes(bytes)
440    }
441}
442
443impl TryFrom<&str> for MacAddress {
444    type Error = MacAddressError;
445
446    fn try_from(s: &str) -> Result<Self, Self::Error> {
447        Self::from_str(s)
448    }
449}
450
451impl From<MacAddress> for [u8; 6] {
452    fn from(mac: MacAddress) -> Self {
453        mac.0
454    }
455}
456
457impl From<[u8; 6]> for MacAddress {
458    fn from(octets: [u8; 6]) -> Self {
459        Self(octets)
460    }
461}
462
463impl FromStr for MacAddress {
464    type Err = MacAddressError;
465
466    fn from_str(s: &str) -> Result<Self, Self::Err> {
467        Self::from_str(s)
468    }
469}
470
471impl fmt::Display for MacAddress {
472    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473        write!(
474            f,
475            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
476            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
477        )
478    }
479}
480
481#[cfg(test)]
482mod tests {
483    use super::*;
484
485    #[test]
486    fn test_new_valid_mac() {
487        assert!(MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).is_ok());
488        assert!(MacAddress::new([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).is_ok());
489        assert!(MacAddress::new([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).is_ok());
490    }
491
492    #[test]
493    fn test_from_bytes_valid() {
494        let bytes = vec![0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
495        assert!(MacAddress::from_bytes(&bytes).is_ok());
496    }
497
498    #[test]
499    fn test_from_bytes_invalid_length() {
500        let bytes = vec![0x00, 0x11, 0x22];
501        assert_eq!(
502            MacAddress::from_bytes(&bytes),
503            Err(MacAddressError::InvalidLength(3))
504        );
505    }
506
507    #[test]
508    fn test_from_str_valid() {
509        assert!(MacAddress::from_str("00:11:22:33:44:55").is_ok());
510        assert!(MacAddress::from_str("00-11-22-33-44-55").is_ok());
511        assert!(MacAddress::from_str("FF:FF:FF:FF:FF:FF").is_ok());
512    }
513
514    #[test]
515    fn test_from_str_invalid_format() {
516        assert_eq!(
517            MacAddress::from_str("00:11:22:33:44"),
518            Err(MacAddressError::InvalidFormat)
519        );
520        assert_eq!(
521            MacAddress::from_str("00:11:22:33:44:55:66"),
522            Err(MacAddressError::InvalidFormat)
523        );
524    }
525
526    #[test]
527    fn test_from_str_invalid_octet() {
528        assert_eq!(
529            MacAddress::from_str("GG:11:22:33:44:55"),
530            Err(MacAddressError::InvalidOctet)
531        );
532    }
533
534    #[test]
535    fn test_as_str() {
536        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
537        assert_eq!(mac.as_str(), "00:11:22:33:44:55");
538    }
539
540    #[test]
541    fn test_as_str_dash() {
542        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
543        assert_eq!(mac.as_str_dash(), "00-11-22-33-44-55");
544    }
545
546    #[test]
547    fn test_as_str_lower() {
548        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
549        assert_eq!(mac.as_str_lower(), "00:11:22:33:44:55");
550    }
551
552    #[test]
553    fn test_as_inner() {
554        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
555        let octets = mac.as_inner();
556        assert_eq!(octets, &[0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
557    }
558
559    #[test]
560    fn test_into_inner() {
561        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
562        let octets = mac.into_inner();
563        assert_eq!(octets, [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
564    }
565
566    #[test]
567    fn test_octets() {
568        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
569        assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
570    }
571
572    #[test]
573    fn test_is_unicast() {
574        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
575        assert!(mac.is_unicast());
576        assert!(!mac.is_multicast());
577    }
578
579    #[test]
580    fn test_is_multicast() {
581        let mac = MacAddress::new([0x01, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
582        assert!(mac.is_multicast());
583        assert!(!mac.is_unicast());
584    }
585
586    #[test]
587    fn test_is_globally_administered() {
588        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
589        assert!(mac.is_globally_administered());
590        assert!(!mac.is_locally_administered());
591    }
592
593    #[test]
594    fn test_is_locally_administered() {
595        let mac = MacAddress::new([0x02, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
596        assert!(mac.is_locally_administered());
597        assert!(!mac.is_globally_administered());
598    }
599
600    #[test]
601    fn test_oui() {
602        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
603        assert_eq!(mac.oui(), [0x00, 0x11, 0x22]);
604    }
605
606    #[test]
607    fn test_nic() {
608        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
609        assert_eq!(mac.nic(), [0x33, 0x44, 0x55]);
610    }
611
612    #[test]
613    fn test_try_from_bytes() {
614        let bytes = vec![0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
615        let mac = MacAddress::try_from(bytes.as_slice()).unwrap();
616        assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
617    }
618
619    #[test]
620    fn test_try_from_str() {
621        let mac = MacAddress::try_from("00:11:22:33:44:55").unwrap();
622        assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
623    }
624
625    #[test]
626    fn test_from_mac_to_array() {
627        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
628        let octets: [u8; 6] = mac.into();
629        assert_eq!(octets, [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
630    }
631
632    #[test]
633    fn test_from_array_to_mac() {
634        let octets = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
635        let mac: MacAddress = octets.into();
636        assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
637    }
638
639    #[test]
640    fn test_from_str() {
641        let mac: MacAddress = "00:11:22:33:44:55".parse().unwrap();
642        assert_eq!(mac.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
643    }
644
645    #[test]
646    fn test_from_str_invalid() {
647        assert!("00:11:22:33:44".parse::<MacAddress>().is_err());
648        assert!("GG:11:22:33:44:55".parse::<MacAddress>().is_err());
649    }
650
651    #[test]
652    fn test_display() {
653        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
654        assert_eq!(format!("{mac}"), "00:11:22:33:44:55");
655    }
656
657    #[test]
658    fn test_equality() {
659        let mac1 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
660        let mac2 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
661        let mac3 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x56]).unwrap();
662
663        assert_eq!(mac1, mac2);
664        assert_ne!(mac1, mac3);
665    }
666
667    #[test]
668    fn test_ordering() {
669        let mac1 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
670        let mac2 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x56]).unwrap();
671
672        assert!(mac1 < mac2);
673    }
674
675    #[test]
676    fn test_clone() {
677        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
678        let mac2 = mac.clone();
679        assert_eq!(mac, mac2);
680    }
681
682    #[test]
683    fn test_error_display() {
684        assert_eq!(
685            format!("{}", MacAddressError::InvalidLength(3)),
686            "MAC address must be 6 bytes (got 3)"
687        );
688        assert_eq!(
689            format!("{}", MacAddressError::InvalidFormat),
690            "MAC address must be in format XX:XX:XX:XX:XX:XX or XX-XX-XX-XX-XX-XX"
691        );
692        assert_eq!(
693            format!("{}", MacAddressError::InvalidOctet),
694            "MAC address octet is invalid (must be 00-FF)"
695        );
696    }
697
698    #[test]
699    fn test_broadcast_address() {
700        let mac = MacAddress::new([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).unwrap();
701        assert!(mac.is_multicast());
702    }
703
704    #[test]
705    fn test_hash() {
706        use core::hash::Hash;
707        use core::hash::Hasher;
708
709        #[derive(Default)]
710        struct SimpleHasher(u64);
711
712        impl Hasher for SimpleHasher {
713            fn finish(&self) -> u64 {
714                self.0
715            }
716
717            fn write(&mut self, bytes: &[u8]) {
718                for byte in bytes {
719                    self.0 = self.0.wrapping_mul(31).wrapping_add(*byte as u64);
720                }
721            }
722        }
723
724        let mac1 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
725        let mac2 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
726        let mac3 = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x56]).unwrap();
727
728        let mut hasher1 = SimpleHasher::default();
729        let mut hasher2 = SimpleHasher::default();
730        let mut hasher3 = SimpleHasher::default();
731
732        mac1.hash(&mut hasher1);
733        mac2.hash(&mut hasher2);
734        mac3.hash(&mut hasher3);
735
736        assert_eq!(hasher1.finish(), hasher2.finish());
737        assert_ne!(hasher1.finish(), hasher3.finish());
738    }
739
740    #[test]
741    fn test_debug() {
742        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
743        assert_eq!(format!("{:?}", mac), "MacAddress([0, 17, 34, 51, 68, 85])");
744    }
745
746    #[test]
747    fn test_from_into_inner_roundtrip() {
748        let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]).unwrap();
749        let octets = mac.into_inner();
750        let mac2 = MacAddress::new(octets).unwrap();
751        assert_eq!(mac2.octets(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
752    }
753}