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