mac_addr/
addr.rs

1#[cfg(all(not(feature = "std"), feature = "alloc"))]
2extern crate alloc;
3
4#[cfg(all(not(feature = "std"), feature = "alloc"))]
5use alloc::format;
6
7use crate::error::ParseMacAddrError;
8use core::fmt;
9use core::str::FromStr;
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
13
14#[cfg(all(feature = "alloc", not(feature = "std")))]
15use alloc as alloc_mod;
16#[cfg(feature = "std")]
17use std as alloc_mod;
18
19#[cfg(any(feature = "std", feature = "alloc"))]
20use alloc_mod::string::String;
21
22/// 48-bit MAC address (IEEE EUI-48).
23#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Debug)]
24pub struct MacAddr(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8);
25
26impl MacAddr {
27    /// Constructs a new [`MacAddr`] from six octets.
28    #[inline]
29    pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> MacAddr {
30        MacAddr(a, b, c, d, e, f)
31    }
32
33    /// Constructs from a `[u8; 6]` array.
34    #[inline]
35    pub fn from_octets(octets: [u8; 6]) -> MacAddr {
36        MacAddr(
37            octets[0], octets[1], octets[2], octets[3], octets[4], octets[5],
38        )
39    }
40
41    /// Returns the 6 octets backing this address.
42    #[inline]
43    pub fn octets(&self) -> [u8; 6] {
44        [self.0, self.1, self.2, self.3, self.4, self.5]
45    }
46
47    /// Returns a colon-separated lowercase hex string (`xx:xx:xx:xx:xx:xx`).
48    #[cfg(any(feature = "std", feature = "alloc"))]
49    #[inline]
50    pub fn address(&self) -> String {
51        format!(
52            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
53            self.0, self.1, self.2, self.3, self.4, self.5
54        )
55    }
56
57    /// Returns the all-zeros address.
58    #[inline]
59    pub fn zero() -> MacAddr {
60        MacAddr(0, 0, 0, 0, 0, 0)
61    }
62
63    /// Returns the broadcast address (`ff:ff:ff:ff:ff:ff`).
64    #[inline]
65    pub fn broadcast() -> MacAddr {
66        MacAddr(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
67    }
68
69    /// Parses a fixed-width hex string (`xx:xx:xx:xx:xx:xx`).
70    #[inline]
71    pub fn from_hex_format(hex_mac_addr: &str) -> MacAddr {
72        if hex_mac_addr.len() != 17 {
73            return MacAddr::zero();
74        }
75        let mut fields = hex_mac_addr.split(':');
76        let o1 = u8::from_str_radix(fields.next().unwrap_or_default(), 16).unwrap_or(0);
77        let o2 = u8::from_str_radix(fields.next().unwrap_or_default(), 16).unwrap_or(0);
78        let o3 = u8::from_str_radix(fields.next().unwrap_or_default(), 16).unwrap_or(0);
79        let o4 = u8::from_str_radix(fields.next().unwrap_or_default(), 16).unwrap_or(0);
80        let o5 = u8::from_str_radix(fields.next().unwrap_or_default(), 16).unwrap_or(0);
81        let o6 = u8::from_str_radix(fields.next().unwrap_or_default(), 16).unwrap_or(0);
82        MacAddr(o1, o2, o3, o4, o5, o6)
83    }
84
85    #[inline]
86    pub fn is_broadcast(&self) -> bool {
87        self.0 == 0xff
88            && self.1 == 0xff
89            && self.2 == 0xff
90            && self.3 == 0xff
91            && self.4 == 0xff
92            && self.5 == 0xff
93    }
94
95    /// Returns `true` if the address is multicast.
96    #[inline]
97    pub fn is_multicast(&self) -> bool {
98        self.0 & 0x01 == 0x01
99    }
100
101    /// Returns `true` if the address is unicast.
102    #[inline]
103    pub fn is_unicast(&self) -> bool {
104        !self.is_multicast() && !self.is_broadcast()
105    }
106
107    /// Returns `true` if the address is locally administered.
108    #[inline]
109    pub fn is_locally_administered(&self) -> bool {
110        self.0 & 0x02 == 0x02
111    }
112
113    /// Returns `true` if the address is universally administered.
114    #[inline]
115    pub fn is_universal(&self) -> bool {
116        !self.is_locally_administered()
117    }
118
119    /// Returns the OUI (first 3 octets).
120    #[inline]
121    pub fn oui(&self) -> [u8; 3] {
122        [self.0, self.1, self.2]
123    }
124}
125
126impl fmt::Display for MacAddr {
127    /// Lowercase hex with `:` separators.
128    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129        let _ = write!(
130            f,
131            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
132            self.0, self.1, self.2, self.3, self.4, self.5
133        );
134        Ok(())
135    }
136}
137
138impl fmt::LowerHex for MacAddr {
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        write!(
141            f,
142            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
143            self.0, self.1, self.2, self.3, self.4, self.5
144        )
145    }
146}
147
148impl fmt::UpperHex for MacAddr {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        write!(
151            f,
152            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
153            self.0, self.1, self.2, self.3, self.4, self.5
154        )
155    }
156}
157
158impl FromStr for MacAddr {
159    type Err = ParseMacAddrError;
160
161    fn from_str(s: &str) -> Result<MacAddr, ParseMacAddrError> {
162        let mut parts = [0u8; 6];
163        let mut i = 0;
164        for split in s.split(':') {
165            if i == 6 {
166                return Err(ParseMacAddrError::TooManyComponents);
167            }
168            match u8::from_str_radix(split, 16) {
169                Ok(b) if !split.is_empty() => parts[i] = b,
170                _ => return Err(ParseMacAddrError::InvalidComponent),
171            }
172            i += 1;
173        }
174        if i == 6 {
175            Ok(MacAddr(
176                parts[0], parts[1], parts[2], parts[3], parts[4], parts[5],
177            ))
178        } else {
179            Err(ParseMacAddrError::TooFewComponents)
180        }
181    }
182}
183
184#[cfg(feature = "serde")]
185impl Serialize for MacAddr {
186    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
187        if serializer.is_human_readable() {
188            serializer.collect_str(self)
189        } else {
190            serializer.serialize_bytes(&[self.0, self.1, self.2, self.3, self.4, self.5])
191        }
192    }
193}
194
195#[cfg(feature = "serde")]
196impl<'de> Deserialize<'de> for MacAddr {
197    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
198        struct MacAddrVisitor;
199        impl<'de> de::Visitor<'de> for MacAddrVisitor {
200            type Value = MacAddr;
201
202            fn visit_str<E: de::Error>(self, value: &str) -> Result<MacAddr, E> {
203                value.parse().map_err(E::custom)
204            }
205            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<MacAddr, E> {
206                if v.len() == 6 {
207                    Ok(MacAddr::new(v[0], v[1], v[2], v[3], v[4], v[5]))
208                } else {
209                    Err(E::invalid_length(v.len(), &self))
210                }
211            }
212            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
213                f.write_str("either a string MAC address or 6-byte array")
214            }
215        }
216
217        if deserializer.is_human_readable() {
218            deserializer.deserialize_str(MacAddrVisitor)
219        } else {
220            deserializer.deserialize_bytes(MacAddrVisitor)
221        }
222    }
223}
224
225impl From<[u8; 6]> for MacAddr {
226    #[inline]
227    fn from(v: [u8; 6]) -> Self {
228        MacAddr::from_octets(v)
229    }
230}
231
232impl From<MacAddr> for [u8; 6] {
233    #[inline]
234    fn from(m: MacAddr) -> Self {
235        m.octets()
236    }
237}
238
239impl TryFrom<&[u8]> for MacAddr {
240    type Error = ();
241
242    #[inline]
243    fn try_from(s: &[u8]) -> Result<Self, Self::Error> {
244        if s.len() == 6 {
245            Ok(MacAddr::new(s[0], s[1], s[2], s[3], s[4], s[5]))
246        } else {
247            Err(())
248        }
249    }
250}
251
252impl AsRef<[u8; 6]> for MacAddr {
253    /// # Safety
254    /// This is a plain `repr(Rust)` tuple struct of six `u8`s.
255    /// Reinterpreting its memory as `[u8; 6]` is layout-compatible for all stable Rust targets.
256    #[inline]
257    fn as_ref(&self) -> &[u8; 6] {
258        unsafe { &*(self as *const MacAddr as *const [u8; 6]) }
259    }
260}