use core::fmt;
use core::str::FromStr;
use crate::error::CrafterError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct MacAddr([u8; 6]);
impl MacAddr {
pub const ZERO: Self = Self([0, 0, 0, 0, 0, 0]);
pub const BROADCAST: Self = Self([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
pub const fn new(octets: [u8; 6]) -> Self {
Self(octets)
}
pub const fn octets(self) -> [u8; 6] {
self.0
}
pub const fn is_broadcast(self) -> bool {
self.0[0] == 0xff
&& self.0[1] == 0xff
&& self.0[2] == 0xff
&& self.0[3] == 0xff
&& self.0[4] == 0xff
&& self.0[5] == 0xff
}
pub const fn is_multicast(self) -> bool {
self.0[0] & 1 == 1
}
}
impl From<[u8; 6]> for MacAddr {
fn from(value: [u8; 6]) -> Self {
Self(value)
}
}
impl From<MacAddr> for [u8; 6] {
fn from(value: MacAddr) -> Self {
value.0
}
}
impl AsRef<[u8; 6]> for MacAddr {
fn as_ref(&self) -> &[u8; 6] {
&self.0
}
}
impl fmt::Display for MacAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
)
}
}
impl FromStr for MacAddr {
type Err = CrafterError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let separator = if input.contains(':') {
':'
} else if input.contains('-') {
'-'
} else {
return Err(CrafterError::invalid_mac_address(
input,
"expected ':' or '-' separators",
));
};
let mut octets = [0u8; 6];
let mut count = 0usize;
for part in input.split(separator) {
if count == octets.len() {
return Err(CrafterError::invalid_mac_address(
input,
"expected exactly six octets",
));
}
if part.len() != 2 {
return Err(CrafterError::invalid_mac_address(
input,
"each octet must contain two hex digits",
));
}
octets[count] = u8::from_str_radix(part, 16).map_err(|_| {
CrafterError::invalid_mac_address(input, "octet contains non-hex digits")
})?;
count += 1;
}
if count != octets.len() {
return Err(CrafterError::invalid_mac_address(
input,
"expected exactly six octets",
));
}
Ok(Self(octets))
}
}
#[cfg(test)]
mod field_primitives {
use super::MacAddr;
#[test]
fn parses_colon_mac_address() {
let mac: MacAddr = "02:00:5e:10:00:01".parse().unwrap();
assert_eq!(mac.octets(), [0x02, 0x00, 0x5e, 0x10, 0x00, 0x01]);
assert_eq!(mac.to_string(), "02:00:5e:10:00:01");
}
#[test]
fn parses_hyphen_mac_address() {
let mac: MacAddr = "AA-BB-CC-DD-EE-FF".parse().unwrap();
assert_eq!(mac.octets(), [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]);
assert_eq!(mac.to_string(), "aa:bb:cc:dd:ee:ff");
}
#[test]
fn rejects_invalid_octet_count() {
let err = "00:11:22:33:44".parse::<MacAddr>().unwrap_err();
assert!(err.to_string().contains("six octets"));
}
#[test]
fn rejects_invalid_hex() {
let err = "00:11:22:33:44:xx".parse::<MacAddr>().unwrap_err();
assert!(err.to_string().contains("non-hex"));
}
#[test]
fn detects_broadcast_and_multicast_addresses() {
assert!(MacAddr::BROADCAST.is_broadcast());
assert!(MacAddr::BROADCAST.is_multicast());
assert!(!MacAddr::ZERO.is_multicast());
}
}