Skip to main content

alioth/device/
net.rs

1// Copyright 2025 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::str::FromStr;
16
17use serde::Deserialize;
18use serde::de::{self, Visitor};
19use serde_aco::{Help, TypedHelp};
20use zerocopy::{FromBytes, Immutable, IntoBytes};
21
22#[derive(Debug, Clone, Default, FromBytes, Immutable, IntoBytes, PartialEq, Eq)]
23#[repr(transparent)]
24pub struct MacAddr(pub [u8; 6]);
25
26#[derive(Debug)]
27pub enum Error {
28    InvalidLength { len: usize },
29    InvalidNumber { num: String },
30}
31
32impl FromStr for MacAddr {
33    type Err = Error;
34
35    fn from_str(s: &str) -> Result<Self, Self::Err> {
36        let mut addr = [0u8; 6];
37        let iter = s.split(':');
38        let mut index = 0;
39        for b_s in iter {
40            let Ok(v) = u8::from_str_radix(b_s, 16) else {
41                return Err(Error::InvalidNumber {
42                    num: b_s.to_owned(),
43                });
44            };
45            if let Some(b) = addr.get_mut(index) {
46                *b = v;
47            };
48            index += 1;
49        }
50        if index != 6 {
51            return Err(Error::InvalidLength { len: index });
52        }
53        Ok(MacAddr(addr))
54    }
55}
56
57impl Help for MacAddr {
58    const HELP: TypedHelp = TypedHelp::Custom { desc: "mac-addr" };
59}
60
61struct MacAddrVisitor;
62
63impl<'de> Visitor<'de> for MacAddrVisitor {
64    type Value = MacAddr;
65
66    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
67        formatter.write_str("a MAC address like ea:d7:a8:e8:c6:2f")
68    }
69
70    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
71    where
72        E: de::Error,
73    {
74        match v.parse::<MacAddr>() {
75            Ok(v) => Ok(v),
76            Err(Error::InvalidLength { len }) => Err(E::invalid_length(len, &"6")),
77            Err(Error::InvalidNumber { num }) => Err(E::invalid_value(
78                de::Unexpected::Str(num.as_str()),
79                &"hexadecimal",
80            )),
81        }
82    }
83}
84
85impl<'de> Deserialize<'de> for MacAddr {
86    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
87    where
88        D: de::Deserializer<'de>,
89    {
90        deserializer.deserialize_str(MacAddrVisitor)
91    }
92}
93
94#[cfg(test)]
95#[path = "net_test.rs"]
96mod tests;