1 // Copyright 2022 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 
15 use std::fmt::Display;
16 
17 use hex::FromHex;
18 use serde::{Deserialize, Serialize};
19 use thiserror::Error;
20 
21 const SHORT_MAC_ADDRESS_SIZE: usize = 2;
22 const SHORT_MAC_ADDRESS_STR_SIZE: usize = 2 * SHORT_MAC_ADDRESS_SIZE;
23 
24 const EXTENDED_MAC_ADDRESS_SIZE: usize = 8;
25 const EXTENDED_MAC_ADDRESS_STR_SIZE: usize = 2 * EXTENDED_MAC_ADDRESS_SIZE;
26 
27 #[derive(Error, Debug)]
28 pub enum Error {
29     #[error("MacAddress has the wrong format: 0")]
30     MacAddressWrongFormat(String),
31 }
32 
33 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
34 #[serde(try_from = "String", into = "String")]
35 pub enum MacAddress {
36     Short([u8; SHORT_MAC_ADDRESS_SIZE]),
37     Extended([u8; EXTENDED_MAC_ADDRESS_SIZE]),
38 }
39 
40 impl MacAddress {
new(mac_address: String) -> Result<Self, Error>41     pub fn new(mac_address: String) -> Result<Self, Error> {
42         mac_address.try_into()
43     }
44 }
45 
46 impl From<&MacAddress> for u64 {
from(mac_address: &MacAddress) -> Self47     fn from(mac_address: &MacAddress) -> Self {
48         match mac_address {
49             MacAddress::Short(addr) => u16::from_le_bytes(*addr) as u64,
50             MacAddress::Extended(addr) => u64::from_le_bytes(*addr),
51         }
52     }
53 }
54 
55 impl From<MacAddress> for u64 {
from(mac_address: MacAddress) -> Self56     fn from(mac_address: MacAddress) -> Self {
57         u64::from(&mac_address)
58     }
59 }
60 
61 impl From<&MacAddress> for Vec<u8> {
from(mac_address: &MacAddress) -> Self62     fn from(mac_address: &MacAddress) -> Self {
63         match mac_address {
64             MacAddress::Short(addr) => addr.to_vec(),
65             MacAddress::Extended(addr) => addr.to_vec(),
66         }
67     }
68 }
69 
70 impl From<MacAddress> for Vec<u8> {
from(mac_address: MacAddress) -> Self71     fn from(mac_address: MacAddress) -> Self {
72         Vec::<u8>::from(&mac_address)
73     }
74 }
75 
76 impl TryFrom<String> for MacAddress {
77     type Error = Error;
try_from(mac_address: String) -> std::result::Result<Self, Error>78     fn try_from(mac_address: String) -> std::result::Result<Self, Error> {
79         let mac_address = mac_address.replace(':', "").replace("%3A", "");
80         Ok(match mac_address.len() {
81             SHORT_MAC_ADDRESS_STR_SIZE => MacAddress::Short(
82                 <[u8; SHORT_MAC_ADDRESS_SIZE]>::from_hex(mac_address)
83                     .map_err(|err| Error::MacAddressWrongFormat(err.to_string()))?,
84             ),
85             EXTENDED_MAC_ADDRESS_STR_SIZE => MacAddress::Extended(
86                 <[u8; EXTENDED_MAC_ADDRESS_SIZE]>::from_hex(mac_address)
87                     .map_err(|err| Error::MacAddressWrongFormat(err.to_string()))?,
88             ),
89             _ => return Err(Error::MacAddressWrongFormat(mac_address)),
90         })
91     }
92 }
93 
94 impl From<&MacAddress> for String {
from(mac_address: &MacAddress) -> Self95     fn from(mac_address: &MacAddress) -> Self {
96         let to_string = |addr: &[u8]| -> String {
97             let mac_address: Vec<_> = addr.iter().map(|byte| format!("{:02X}:", byte)).collect();
98             let s = mac_address
99                 .iter()
100                 .flat_map(|byte| byte.chars())
101                 .collect::<String>();
102             s.trim_end_matches(':').into()
103         };
104         match mac_address {
105             MacAddress::Short(address) => to_string(address),
106             MacAddress::Extended(address) => to_string(address),
107         }
108     }
109 }
110 
111 impl From<MacAddress> for String {
from(mac_address: MacAddress) -> Self112     fn from(mac_address: MacAddress) -> Self {
113         String::from(&mac_address)
114     }
115 }
116 
117 impl Display for MacAddress {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result118     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119         write!(f, "{}", String::from(self))
120     }
121 }
122 
123 #[cfg(test)]
124 mod tests {
125     use super::*;
126 
127     #[test]
valid_mac_address()128     fn valid_mac_address() {
129         let valid_mac_address = "00:11";
130         assert_eq!(
131             MacAddress::new(valid_mac_address.into()).unwrap(),
132             MacAddress::Short([0x00, 0x11])
133         );
134         let valid_mac_address = "FF:77:AA:DD:EE:BB:CC:10";
135         assert_eq!(
136             MacAddress::new(valid_mac_address.into()).unwrap(),
137             MacAddress::Extended([0xFF, 0x77, 0xAA, 0xDD, 0xEE, 0xBB, 0xCC, 0x10])
138         );
139     }
140 
141     #[test]
142     #[should_panic]
invalid_mac_address_short()143     fn invalid_mac_address_short() {
144         let invalid_mac_address = "00:11:22";
145         MacAddress::new(invalid_mac_address.into()).unwrap();
146     }
147 
148     #[test]
149     #[should_panic]
invalid_mac_address_extend()150     fn invalid_mac_address_extend() {
151         let invalid_mac_address = "00:11:22:33:44:55:66";
152         MacAddress::new(invalid_mac_address.into()).unwrap();
153     }
154 
155     #[test]
display_mac_address()156     fn display_mac_address() {
157         let extended_mac_address = "00:FF:77:AA:DD:EE:CC:45";
158         let short_mac_address = "00:FF";
159         assert_eq!(
160             format!("{}", MacAddress::new(extended_mac_address.into()).unwrap()),
161             extended_mac_address
162         );
163         assert_eq!(
164             format!("{}", MacAddress::new(short_mac_address.into()).unwrap()),
165             short_mac_address
166         );
167         assert_eq!(extended_mac_address.to_string(), extended_mac_address);
168     }
169 
170     #[test]
test_short_mac_to_u64()171     fn test_short_mac_to_u64() {
172         let short_mac = MacAddress::Short([0x01, 0x02]);
173         let result: u64 = short_mac.into();
174         let expected: u64 = 0x0201;
175         assert_eq!(result, expected);
176     }
177 
178     #[test]
test_extended_mac_to_u64()179     fn test_extended_mac_to_u64() {
180         let extended_mac = MacAddress::Extended([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
181         let result: u64 = extended_mac.into();
182         let expected: u64 = 0x0807060504030201;
183         assert_eq!(result, expected);
184     }
185 }
186