blob: 320631a91aef0059a426998897951066d97166d3 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::fmt::Display;
use hex::FromHex;
use serde::{Deserialize, Serialize};
use thiserror::Error;
const SHORT_MAC_ADDRESS_SIZE: usize = 2;
const STRING_SHORT_MAC_ADDRESS_SIZE: usize = 2 * SHORT_MAC_ADDRESS_SIZE;
const EXTEND_MAC_ADDRESS_SIZE: usize = 8;
const STRING_EXTEND_MAC_ADDRESS_SIZE: usize = 2 * EXTEND_MAC_ADDRESS_SIZE;
#[derive(Error, Debug)]
pub enum Error {
#[error("MacAddress has the wrong format: 0")]
MacAddressWrongFormat(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(try_from = "String", into = "String")]
pub enum MacAddress {
Short([u8; SHORT_MAC_ADDRESS_SIZE]),
Extend([u8; EXTEND_MAC_ADDRESS_SIZE]),
}
impl MacAddress {
pub fn new(mac_address: String) -> Result<Self, Error> {
mac_address.try_into()
}
}
impl From<usize> for MacAddress {
fn from(device_handle: usize) -> Self {
let handle = device_handle as u64;
MacAddress::Extend(handle.to_be_bytes())
}
}
impl TryFrom<String> for MacAddress {
type Error = Error;
fn try_from(mac_address: String) -> std::result::Result<Self, Error> {
let mac_address = mac_address.replace(':', "");
let mac_address = mac_address.replace("%3A", "");
let uwb_mac_address = match mac_address.len() {
STRING_SHORT_MAC_ADDRESS_SIZE => MacAddress::Short(
<[u8; SHORT_MAC_ADDRESS_SIZE]>::from_hex(mac_address)
.map_err(|err| Error::MacAddressWrongFormat(err.to_string()))?,
),
STRING_EXTEND_MAC_ADDRESS_SIZE => MacAddress::Extend(
<[u8; EXTEND_MAC_ADDRESS_SIZE]>::from_hex(mac_address)
.map_err(|err| Error::MacAddressWrongFormat(err.to_string()))?,
),
_ => return Err(Error::MacAddressWrongFormat(mac_address)),
};
Ok(uwb_mac_address)
}
}
impl From<&MacAddress> for String {
fn from(mac_address: &MacAddress) -> Self {
let to_string = |addr: &[u8]| -> String {
let mac_address: Vec<_> = addr.iter().map(|byte| format!("{:02X}:", byte)).collect();
let s = mac_address
.iter()
.flat_map(|byte| byte.chars())
.collect::<String>();
s.trim_end_matches(':').into()
};
match mac_address {
MacAddress::Short(address) => to_string(address),
MacAddress::Extend(address) => to_string(address),
}
}
}
impl From<MacAddress> for String {
fn from(mac_address: MacAddress) -> Self {
String::from(&mac_address)
}
}
impl Display for MacAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", String::from(self))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_mac_address() {
let valid_mac_address = "00:11";
assert_eq!(
MacAddress::new(valid_mac_address.into()).unwrap(),
MacAddress::Short([0x00, 0x11])
);
let valid_mac_address = "FF:77:AA:DD:EE:BB:CC:10";
assert_eq!(
MacAddress::new(valid_mac_address.into()).unwrap(),
MacAddress::Extend([0xFF, 0x77, 0xAA, 0xDD, 0xEE, 0xBB, 0xCC, 0x10])
);
}
#[test]
#[should_panic]
fn invalid_mac_address_short() {
let invalid_mac_address = "00:11:22";
MacAddress::new(invalid_mac_address.into()).unwrap();
}
#[test]
#[should_panic]
fn invalid_mac_address_extend() {
let invalid_mac_address = "00:11:22:33:44:55:66";
MacAddress::new(invalid_mac_address.into()).unwrap();
}
#[test]
fn display_mac_address() {
let extend_mac_address = "00:FF:77:AA:DD:EE:CC:45";
let short_mac_address = "00:FF";
assert_eq!(
format!("{}", MacAddress::new(extend_mac_address.into()).unwrap()),
extend_mac_address
);
assert_eq!(
format!("{}", MacAddress::new(short_mac_address.into()).unwrap()),
short_mac_address
);
assert_eq!(extend_mac_address.to_string(), extend_mac_address);
}
}