use std::str::FromStr;
use serde::Deserialize;
use serde::de::{self, Visitor};
use serde_aco::{Help, TypedHelp};
use zerocopy::{FromBytes, Immutable, IntoBytes};
#[derive(Debug, Clone, Default, FromBytes, Immutable, IntoBytes, PartialEq, Eq)]
#[repr(transparent)]
pub struct MacAddr(pub [u8; 6]);
#[derive(Debug)]
pub enum Error {
InvalidLength { len: usize },
InvalidNumber { num: String },
}
impl FromStr for MacAddr {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut addr = [0u8; 6];
let iter = s.split(':');
let mut index = 0;
for b_s in iter {
let Ok(v) = u8::from_str_radix(b_s, 16) else {
return Err(Error::InvalidNumber {
num: b_s.to_owned(),
});
};
if let Some(b) = addr.get_mut(index) {
*b = v;
};
index += 1;
}
if index != 6 {
return Err(Error::InvalidLength { len: index });
}
Ok(MacAddr(addr))
}
}
impl Help for MacAddr {
const HELP: TypedHelp = TypedHelp::Custom { desc: "mac-addr" };
}
struct MacAddrVisitor;
impl<'de> Visitor<'de> for MacAddrVisitor {
type Value = MacAddr;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a MAC address like ea:d7:a8:e8:c6:2f")
}
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: de::Error,
{
match v.parse::<MacAddr>() {
Ok(v) => Ok(v),
Err(Error::InvalidLength { len }) => Err(E::invalid_length(len, &"6")),
Err(Error::InvalidNumber { num }) => Err(E::invalid_value(
de::Unexpected::Str(num.as_str()),
&"hexadecimal",
)),
}
}
}
impl<'de> Deserialize<'de> for MacAddr {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
deserializer.deserialize_str(MacAddrVisitor)
}
}
#[cfg(test)]
#[path = "net_test.rs"]
mod tests;