mac_addr/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(all(not(feature = "std"), feature = "alloc"))]
4extern crate alloc;
5
6#[cfg(all(not(feature = "std"), feature = "alloc"))]
7use alloc::format;
8
9use core::fmt;
10use core::str::FromStr;
11
12#[cfg(feature = "serde")]
13use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
14
15#[cfg(all(feature = "std", not(feature = "alloc")))]
16use std as alloc_mod;
17#[cfg(all(feature = "alloc", not(feature = "std")))]
18use alloc as alloc_mod;
19
20#[cfg(any(feature = "std", feature = "alloc"))]
21use alloc_mod::string::String;
22
23/// 48-bit MAC address (IEEE EUI-48).
24/// Suitable for FFI and network code.
25#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Debug)]
26pub struct MacAddr(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8);
27
28impl MacAddr {
29    /// Constructs a new [`MacAddr`] from six octets.
30    #[inline]
31    pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> MacAddr {
32        MacAddr(a, b, c, d, e, f)
33    }
34
35    /// Constructs from a `[u8; 6]` array.
36    #[inline]
37    pub fn from_octets(octets: [u8; 6]) -> MacAddr {
38        MacAddr(octets[0], octets[1], octets[2], octets[3], octets[4], octets[5])
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    /// Returns `true` if the address is multicast.
86    #[inline]
87    pub fn is_multicast(&self) -> bool {
88        self.0 & 0x01 == 0x01
89    }
90
91    /// Returns `true` if the address is unicast.
92    #[inline]
93    pub fn is_unicast(&self) -> bool {
94        !self.is_multicast()
95    }
96
97    /// Returns `true` if the address is locally administered.
98    #[inline]
99    pub fn is_locally_administered(&self) -> bool {
100        self.0 & 0x02 == 0x02
101    }
102
103    /// Returns `true` if the address is universally administered.
104    #[inline]
105    pub fn is_universal(&self) -> bool {
106        !self.is_locally_administered()
107    }
108
109    /// Returns the OUI (first 3 octets).
110    #[inline]
111    pub fn oui(&self) -> [u8; 3] {
112        [self.0, self.1, self.2]
113    }
114}
115
116impl fmt::Display for MacAddr {
117    /// Lowercase hex with `:` separators.
118    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119        let _ = write!(
120            f,
121            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
122            self.0, self.1, self.2, self.3, self.4, self.5
123        );
124        Ok(())
125    }
126}
127
128/// Error returned when parsing a MAC address with [`FromStr`].
129#[derive(Copy, Debug, PartialEq, Eq, Clone)]
130pub enum ParseMacAddrError {
131    /// Input contained more than 6 components.
132    TooManyComponents,
133    /// Input contained fewer than 6 components.
134    TooFewComponents,
135    /// A component was invalid hex or empty.
136    InvalidComponent,
137}
138
139impl fmt::Display for ParseMacAddrError {
140    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141        let s = match *self {
142            ParseMacAddrError::TooManyComponents => "Too many components in a MAC address string",
143            ParseMacAddrError::TooFewComponents => "Too few components in a MAC address string",
144            ParseMacAddrError::InvalidComponent => "Invalid component in a MAC address string",
145        };
146        f.write_str(s)
147    }
148}
149
150#[cfg(feature = "std")]
151impl std::error::Error for ParseMacAddrError {}
152
153impl FromStr for MacAddr {
154    type Err = ParseMacAddrError;
155
156    fn from_str(s: &str) -> Result<MacAddr, ParseMacAddrError> {
157        let mut parts = [0u8; 6];
158        let mut i = 0;
159        for split in s.split(':') {
160            if i == 6 {
161                return Err(ParseMacAddrError::TooManyComponents);
162            }
163            match u8::from_str_radix(split, 16) {
164                Ok(b) if !split.is_empty() => parts[i] = b,
165                _ => return Err(ParseMacAddrError::InvalidComponent),
166            }
167            i += 1;
168        }
169        if i == 6 {
170            Ok(MacAddr(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]))
171        } else {
172            Err(ParseMacAddrError::TooFewComponents)
173        }
174    }
175}
176
177#[cfg(feature = "serde")]
178impl Serialize for MacAddr {
179    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
180        if serializer.is_human_readable() {
181            serializer.collect_str(self)
182        } else {
183            serializer.serialize_bytes(&[self.0, self.1, self.2, self.3, self.4, self.5])
184        }
185    }
186}
187
188#[cfg(feature = "serde")]
189impl<'de> Deserialize<'de> for MacAddr {
190    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
191        struct MacAddrVisitor;
192        impl<'de> de::Visitor<'de> for MacAddrVisitor {
193            type Value = MacAddr;
194
195            fn visit_str<E: de::Error>(self, value: &str) -> Result<MacAddr, E> {
196                value.parse().map_err(E::custom)
197            }
198            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<MacAddr, E> {
199                if v.len() == 6 {
200                    Ok(MacAddr::new(v[0], v[1], v[2], v[3], v[4], v[5]))
201                } else {
202                    Err(E::invalid_length(v.len(), &self))
203                }
204            }
205            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
206                f.write_str("either a string MAC address or 6-byte array")
207            }
208        }
209
210        if deserializer.is_human_readable() {
211            deserializer.deserialize_str(MacAddrVisitor)
212        } else {
213            deserializer.deserialize_bytes(MacAddrVisitor)
214        }
215    }
216}
217
218impl From<[u8; 6]> for MacAddr {
219    #[inline]
220    fn from(v: [u8; 6]) -> Self { MacAddr::from_octets(v) }
221}
222
223impl From<MacAddr> for [u8; 6] {
224    #[inline]
225    fn from(m: MacAddr) -> Self { m.octets() }
226}
227
228impl AsRef<[u8; 6]> for MacAddr {
229    /// # Safety
230    /// This is a plain `repr(Rust)` tuple struct of six `u8`s. 
231    /// Reinterpreting its memory as `[u8; 6]` is layout-compatible for all stable Rust targets.
232    #[inline]
233    fn as_ref(&self) -> &[u8; 6] {
234        unsafe { &*(self as *const MacAddr as *const [u8; 6]) }
235    }
236}