use core::fmt;
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
pub struct MacAddr(pub [u8; 6]);
pub const BROADCAST_MAC: MacAddr = MacAddr([0xff; 6]);
impl MacAddr {
#[inline]
pub const fn new(octets: [u8; 6]) -> MacAddr {
MacAddr(octets)
}
#[inline]
pub const fn zero() -> MacAddr {
MacAddr([0; 6])
}
#[inline]
pub const fn broadcast() -> MacAddr {
BROADCAST_MAC
}
#[inline]
pub const fn octets(self) -> [u8; 6] {
self.0
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
&self.0
}
pub fn from_slice(b: &[u8]) -> Option<MacAddr> {
if b.len() != 6 {
return None;
}
let mut o = [0u8; 6];
o.copy_from_slice(b);
Some(MacAddr(o))
}
#[inline]
pub fn is_broadcast(self) -> bool {
self.0 == [0xff; 6]
}
#[inline]
pub fn is_multicast(self) -> bool {
self.0[0] & 1 != 0
}
#[inline]
pub fn is_unicast(self) -> bool {
!self.is_multicast()
}
#[inline]
pub fn is_local(self) -> bool {
self.0[0] & 2 != 0
}
pub fn random_local_unicast() -> MacAddr {
let mut o = [0u8; 6];
crate::rand::fill(&mut o);
o[0] = (o[0] & 0xFC) | 0x02;
MacAddr(o)
}
}
impl fmt::Debug for MacAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for MacAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let b = self.0;
write!(
f,
"{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
b[0], b[1], b[2], b[3], b[4], b[5]
)
}
}
impl core::str::FromStr for MacAddr {
type Err = ParseMacError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut octets = [0u8; 6];
let mut i = 0;
for part in s.split([':', '-']) {
if i >= 6 {
return Err(ParseMacError(()));
}
if part.len() != 2 {
return Err(ParseMacError(()));
}
octets[i] = u8::from_str_radix(part, 16).map_err(|_| ParseMacError(()))?;
i += 1;
}
if i != 6 {
return Err(ParseMacError(()));
}
Ok(MacAddr(octets))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParseMacError(());
impl fmt::Display for ParseMacError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("invalid MAC address syntax")
}
}
impl std::error::Error for ParseMacError {}
impl From<[u8; 6]> for MacAddr {
#[inline]
fn from(o: [u8; 6]) -> MacAddr {
MacAddr(o)
}
}
impl From<MacAddr> for [u8; 6] {
#[inline]
fn from(m: MacAddr) -> [u8; 6] {
m.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_and_display_roundtrip() {
let m: MacAddr = "01:23:45:67:89:ab".parse().unwrap();
assert_eq!(m.octets(), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab]);
assert_eq!(format!("{}", m), "01:23:45:67:89:ab");
let n: MacAddr = "01-23-45-67-89-AB".parse().unwrap();
assert_eq!(n, m);
}
#[test]
fn parse_rejects_bad_input() {
assert!("xx:yy:zz:00:00:00".parse::<MacAddr>().is_err());
assert!("01:02:03:04:05".parse::<MacAddr>().is_err());
assert!("01:02:03:04:05:06:07".parse::<MacAddr>().is_err());
assert!("0:1:2:3:4:5".parse::<MacAddr>().is_err()); }
#[test]
fn classify() {
assert!(BROADCAST_MAC.is_broadcast());
assert!(BROADCAST_MAC.is_multicast());
assert!(!MacAddr::zero().is_broadcast());
assert!(!MacAddr::zero().is_multicast());
let m: MacAddr = "01:00:5e:00:00:01".parse().unwrap(); assert!(m.is_multicast());
assert!(!m.is_unicast());
}
}