fips_core/transport/ble/
addr.rs1use crate::transport::{TransportAddr, TransportError};
6
7#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct BleAddr {
10 pub adapter: String,
12 pub device: [u8; 6],
14}
15
16impl BleAddr {
17 pub fn parse(s: &str) -> Result<Self, TransportError> {
19 let (adapter, mac_str) = s.split_once('/').ok_or_else(|| {
20 TransportError::InvalidAddress(format!("missing '/' in BLE address: {s}"))
21 })?;
22
23 if adapter.is_empty() {
24 return Err(TransportError::InvalidAddress("empty adapter name".into()));
25 }
26
27 let device = parse_mac(mac_str).ok_or_else(|| {
28 TransportError::InvalidAddress(format!("invalid MAC address: {mac_str}"))
29 })?;
30
31 Ok(Self {
32 adapter: adapter.to_string(),
33 device,
34 })
35 }
36
37 pub fn to_string_repr(&self) -> String {
39 format!(
40 "{}/{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
41 self.adapter,
42 self.device[0],
43 self.device[1],
44 self.device[2],
45 self.device[3],
46 self.device[4],
47 self.device[5],
48 )
49 }
50
51 pub fn to_transport_addr(&self) -> TransportAddr {
53 TransportAddr::from_string(&self.to_string_repr())
54 }
55}
56
57#[cfg(bluer_available)]
62impl BleAddr {
63 pub fn from_bluer(addr: bluer::Address, adapter: &str) -> Self {
65 Self {
66 adapter: adapter.to_string(),
67 device: addr.0,
68 }
69 }
70
71 pub fn to_bluer_address(&self) -> bluer::Address {
73 bluer::Address(self.device)
74 }
75
76 pub fn to_socket_addr(&self, psm: u16) -> bluer::l2cap::SocketAddr {
78 bluer::l2cap::SocketAddr::new(self.to_bluer_address(), bluer::AddressType::LePublic, psm)
79 }
80}
81
82impl std::fmt::Display for BleAddr {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 write!(f, "{}", self.to_string_repr())
85 }
86}
87
88fn parse_mac(s: &str) -> Option<[u8; 6]> {
90 let parts: Vec<&str> = s.split(':').collect();
91 if parts.len() != 6 {
92 return None;
93 }
94 let mut mac = [0u8; 6];
95 for (i, part) in parts.iter().enumerate() {
96 mac[i] = u8::from_str_radix(part, 16).ok()?;
97 }
98 Some(mac)
99}
100
101pub fn adapter_from_addr(addr: &TransportAddr) -> Option<&str> {
105 addr.as_str()?.split_once('/').map(|(adapter, _)| adapter)
106}
107
108#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn test_parse_valid() {
118 let addr = BleAddr::parse("hci0/AA:BB:CC:DD:EE:FF").unwrap();
119 assert_eq!(addr.adapter, "hci0");
120 assert_eq!(addr.device, [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
121 }
122
123 #[test]
124 fn test_parse_lowercase() {
125 let addr = BleAddr::parse("hci1/aa:bb:cc:dd:ee:ff").unwrap();
126 assert_eq!(addr.adapter, "hci1");
127 assert_eq!(addr.device, [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
128 }
129
130 #[test]
131 fn test_roundtrip() {
132 let original = "hci0/AA:BB:CC:DD:EE:FF";
133 let addr = BleAddr::parse(original).unwrap();
134 assert_eq!(addr.to_string_repr(), original);
135 }
136
137 #[test]
138 fn test_display() {
139 let addr = BleAddr::parse("hci0/01:02:03:04:05:06").unwrap();
140 assert_eq!(format!("{addr}"), "hci0/01:02:03:04:05:06");
141 }
142
143 #[test]
144 fn test_to_transport_addr() {
145 let addr = BleAddr::parse("hci0/AA:BB:CC:DD:EE:FF").unwrap();
146 let ta = addr.to_transport_addr();
147 assert_eq!(ta.as_str(), Some("hci0/AA:BB:CC:DD:EE:FF"));
148 }
149
150 #[test]
151 fn test_parse_missing_slash() {
152 assert!(BleAddr::parse("hci0-AA:BB:CC:DD:EE:FF").is_err());
153 }
154
155 #[test]
156 fn test_parse_empty_adapter() {
157 assert!(BleAddr::parse("/AA:BB:CC:DD:EE:FF").is_err());
158 }
159
160 #[test]
161 fn test_parse_invalid_mac_short() {
162 assert!(BleAddr::parse("hci0/AA:BB:CC").is_err());
163 }
164
165 #[test]
166 fn test_parse_invalid_mac_hex() {
167 assert!(BleAddr::parse("hci0/GG:HH:II:JJ:KK:LL").is_err());
168 }
169
170 #[test]
171 fn test_adapter_from_addr() {
172 let ta = TransportAddr::from_string("hci0/AA:BB:CC:DD:EE:FF");
173 assert_eq!(adapter_from_addr(&ta), Some("hci0"));
174 }
175
176 #[test]
177 fn test_adapter_from_addr_no_slash() {
178 let ta = TransportAddr::from_string("invalid");
179 assert_eq!(adapter_from_addr(&ta), None);
180 }
181}