1use std::convert::{TryFrom, TryInto};
4use std::fmt::{self, Debug, Display, Formatter, LowerHex, UpperHex};
5use std::str::FromStr;
6
7#[derive(Copy, Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub struct BDAddr {
10 address: [u8; 6],
11}
12
13#[derive(Debug, thiserror::Error, Clone, PartialEq)]
15pub enum ParseBDAddrError {
16 #[error("Bluetooth address has to be 6 bytes long")]
17 IncorrectByteCount,
18 #[error("Invalid digit in address: {0}")]
19 InvalidDigit(#[from] std::num::ParseIntError),
20}
21
22impl Display for BDAddr {
23 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
24 <Self as UpperHex>::fmt(self, f)
25 }
26}
27
28impl LowerHex for BDAddr {
29 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
30 let a = &self.address;
31 write!(
32 f,
33 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
34 a[0], a[1], a[2], a[3], a[4], a[5]
35 )
36 }
37}
38
39impl UpperHex for BDAddr {
40 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
41 let a = &self.address;
42 write!(
43 f,
44 "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
45 a[0], a[1], a[2], a[3], a[4], a[5]
46 )
47 }
48}
49
50impl Debug for BDAddr {
51 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
52 <Self as Display>::fmt(self, f)
53 }
54}
55
56impl AsRef<[u8]> for BDAddr {
57 fn as_ref(&self) -> &[u8] {
58 &self.address
59 }
60}
61
62impl From<[u8; 6]> for BDAddr {
63 fn from(address: [u8; 6]) -> Self {
75 Self { address }
76 }
77}
78
79impl<'a> TryFrom<&'a [u8]> for BDAddr {
80 type Error = ParseBDAddrError;
81
82 fn try_from(slice: &'a [u8]) -> Result<Self, Self::Error> {
83 Ok(Self {
84 address: slice
85 .try_into()
86 .map_err(|_| ParseBDAddrError::IncorrectByteCount)?,
87 })
88 }
89}
90
91impl TryFrom<u64> for BDAddr {
92 type Error = ParseBDAddrError;
93
94 fn try_from(int: u64) -> Result<Self, Self::Error> {
95 let slice = int.to_be_bytes(); if slice[0..2] == [0, 0] {
97 Ok(Self {
98 address: slice[2..].try_into().unwrap(),
99 })
100 } else {
101 Err(ParseBDAddrError::IncorrectByteCount)
102 }
103 }
104}
105
106impl From<BDAddr> for u64 {
107 fn from(addr: BDAddr) -> Self {
108 let mut slice = [0; 8];
109 slice[2..].copy_from_slice(&addr.into_inner());
110 u64::from_be_bytes(slice)
111 }
112}
113
114impl FromStr for BDAddr {
115 type Err = ParseBDAddrError;
116
117 fn from_str(s: &str) -> Result<Self, Self::Err> {
122 if s.contains(':') {
123 Self::from_str_delim(s)
124 } else {
125 Self::from_str_no_delim(s)
126 }
127 }
128}
129
130impl BDAddr {
131 pub fn into_inner(self) -> [u8; 6] {
133 self.address
134 }
135
136 pub fn is_random_static(&self) -> bool {
138 self.address[5] & 0b11 == 0b11
139 }
140
141 pub fn from_str_delim(s: &str) -> Result<Self, ParseBDAddrError> {
145 let bytes = s
146 .split(':')
147 .map(|part: &str| u8::from_str_radix(part, 16))
148 .collect::<Result<Vec<u8>, _>>()?;
149
150 if bytes.len() == 6 {
151 let mut address = [0; 6];
152 address.copy_from_slice(bytes.as_slice());
153 Ok(BDAddr { address })
154 } else {
155 Err(ParseBDAddrError::IncorrectByteCount)
156 }
157 }
158
159 pub fn from_str_no_delim(s: &str) -> Result<Self, ParseBDAddrError> {
163 if s.len() != 12 {
164 return Err(ParseBDAddrError::IncorrectByteCount);
165 }
166
167 let mut address = [0; 6];
168 let mut cur = s;
169 for byte in address.iter_mut() {
170 let (part, rest) = cur.split_at(2);
171 *byte = u8::from_str_radix(part, 16)?;
172 cur = rest;
173 }
174 Ok(Self { address })
175 }
176
177 pub fn write_no_delim(&self, f: &mut impl fmt::Write) -> fmt::Result {
179 for b in &self.address {
180 write!(f, "{:02x}", b)?;
181 }
182 Ok(())
183 }
184
185 pub fn to_string_no_delim(&self) -> String {
190 let mut s = String::with_capacity(12);
191 self.write_no_delim(&mut s)
192 .expect("A String-Writer never fails");
193 s
194 }
195}
196
197#[cfg(feature = "serde")]
199pub mod serde {
200 use std::fmt::{self, Write as _};
201
202 use serde::{
203 de::{Deserialize, Deserializer, Error as DeError, Visitor},
204 ser::{Serialize, Serializer},
205 };
206 use serde_cr as serde;
207
208 use super::*;
209
210 impl Serialize for BDAddr {
211 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
212 where
213 S: Serializer,
214 {
215 colon_delim::serialize(self, serializer)
216 }
217 }
218
219 impl<'de> Deserialize<'de> for BDAddr {
220 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
221 where
222 D: Deserializer<'de>,
223 {
224 colon_delim::deserialize(deserializer)
225 }
226 }
227
228 pub mod colon_delim {
251 use super::*;
252
253 struct ColonDelimVisitor;
254
255 pub fn serialize<S>(addr: &BDAddr, serializer: S) -> Result<S::Ok, S::Error>
256 where
257 S: Serializer,
258 {
259 let mut buf = String::with_capacity(17);
260 write!(&mut buf, "{:X}", addr).expect("never fails to write to string");
261 serializer.serialize_str(&buf)
262 }
263
264 pub fn deserialize<'de, D>(d: D) -> Result<BDAddr, D::Error>
265 where
266 D: Deserializer<'de>,
267 {
268 d.deserialize_str(ColonDelimVisitor)
269 }
270
271 impl<'de> Visitor<'de> for ColonDelimVisitor {
272 type Value = BDAddr;
273
274 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
275 write!(
276 formatter,
277 "A colon seperated Bluetooth address, like `00:11:22:33:44:55`"
278 )
279 }
280
281 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
282 where
283 E: DeError,
284 {
285 BDAddr::from_str_delim(v).map_err(E::custom)
286 }
287
288 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
289 where
290 E: DeError,
291 {
292 BDAddr::from_str_delim(v).map_err(E::custom)
293 }
294
295 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
296 where
297 E: DeError,
298 {
299 BDAddr::from_str_delim(&v).map_err(E::custom)
300 }
301 }
302 }
303
304 pub mod no_delim {
326 use super::*;
327
328 struct NoDelimVisitor;
329
330 pub fn serialize<S>(addr: &BDAddr, serializer: S) -> Result<S::Ok, S::Error>
331 where
332 S: Serializer,
333 {
334 let mut buf = String::with_capacity(12);
335 addr.write_no_delim(&mut buf)
336 .expect("never fails to write to string");
337 serializer.serialize_str(&buf)
338 }
339
340 pub fn deserialize<'de, D>(d: D) -> Result<BDAddr, D::Error>
341 where
342 D: Deserializer<'de>,
343 {
344 d.deserialize_str(NoDelimVisitor)
345 }
346
347 impl<'de> Visitor<'de> for NoDelimVisitor {
348 type Value = BDAddr;
349
350 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
351 write!(
352 formatter,
353 "A Bluetooth address without any delimiters, like `001122334455`"
354 )
355 }
356
357 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
358 where
359 E: DeError,
360 {
361 BDAddr::from_str_no_delim(v).map_err(E::custom)
362 }
363
364 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
365 where
366 E: DeError,
367 {
368 BDAddr::from_str_no_delim(v).map_err(E::custom)
369 }
370
371 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
372 where
373 E: DeError,
374 {
375 BDAddr::from_str_no_delim(&v).map_err(E::custom)
376 }
377 }
378 }
379
380 pub mod bytes {
402 use super::*;
403
404 pub fn serialize<S>(addr: &BDAddr, serializer: S) -> Result<S::Ok, S::Error>
405 where
406 S: Serializer,
407 {
408 addr.address.serialize(serializer)
409 }
410
411 pub fn deserialize<'de, D>(d: D) -> Result<BDAddr, D::Error>
412 where
413 D: Deserializer<'de>,
414 {
415 Ok(<[u8; 6]>::deserialize(d)?.into())
416 }
417 }
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423
424 const ADDR: BDAddr = BDAddr {
426 address: [0x1f, 0x2a, 0x00, 0xcc, 0x22, 0xf1],
427 };
428 const HEX: u64 = 0x00_00_1f_2a_00_cc_22_f1;
430
431 #[test]
432 fn parse_addr() {
433 let addr = BDAddr::from([0x2a, 0x00, 0xaa, 0xbb, 0xcc, 0xdd]);
434
435 let result: Result<BDAddr, _> = "2a:00:aa:bb:cc:dd".parse();
436 assert_eq!(result, Ok(addr));
437 let result: Result<BDAddr, _> = "2a00AabbCcdd".parse();
438 assert_eq!(result, Ok(addr));
439 let result: Result<BDAddr, _> = "2A:00:00".parse();
440 assert_eq!(result, Err(ParseBDAddrError::IncorrectByteCount));
441 let result: Result<BDAddr, _> = "2A:00:AA:BB:CC:ZZ".parse();
442 assert!(matches!(result, Err(ParseBDAddrError::InvalidDigit(_))));
443 let result: Result<BDAddr, _> = "2A00aABbcCZz".parse();
444 assert!(matches!(result, Err(ParseBDAddrError::InvalidDigit(_))));
445 }
446
447 #[test]
448 fn display_addr() {
449 assert_eq!(format!("{}", ADDR), "1F:2A:00:CC:22:F1");
450 assert_eq!(format!("{:?}", ADDR), "1F:2A:00:CC:22:F1");
451 assert_eq!(format!("{:x}", ADDR), "1f:2a:00:cc:22:f1");
452 assert_eq!(format!("{:X}", ADDR), "1F:2A:00:CC:22:F1");
453 assert_eq!(format!("{}", ADDR.to_string_no_delim()), "1f2a00cc22f1");
454 }
455
456 #[test]
457 fn u64_to_addr() {
458 let hex_addr: BDAddr = HEX.try_into().unwrap();
459 assert_eq!(hex_addr, ADDR);
460
461 let hex_back: u64 = hex_addr.into();
462 assert_eq!(HEX, hex_back);
463 }
464
465 #[test]
466 fn invalid_u64_to_addr() {
467 assert_eq!(
468 BDAddr::try_from(0x1122334455667788),
469 Err(ParseBDAddrError::IncorrectByteCount)
470 );
471 }
472
473 #[test]
474 fn addr_to_u64() {
475 let addr_as_hex: u64 = ADDR.into();
476 assert_eq!(HEX, addr_as_hex);
477
478 let addr_back: BDAddr = addr_as_hex.try_into().unwrap();
479 assert_eq!(ADDR, addr_back);
480 }
481
482 #[cfg(feature = "serde")]
483 #[test]
484 fn deserialize_toml_delim_bdaddr_with_struct() {
485 use serde_cr::Deserialize;
486
487 #[derive(Deserialize, PartialEq, Copy, Clone, Debug)]
488 #[serde(crate = "serde_cr")]
489 struct Data {
490 addr: BDAddr,
491 }
492
493 let data = Data {
494 addr: BDAddr::from([0xff, 0x00, 0xff, 0x00, 0xff, 0x00]),
495 };
496
497 assert_eq!(toml::from_str(r#"addr = "ff:00:ff:00:ff:00""#), Ok(data));
498 assert!(
499 matches!(toml::from_str::<Data>(r"addr = 0"), Err(e) if e.message().contains("A colon seperated Bluetooth address, like `00:11:22:33:44:55`"))
500 );
501 }
502
503 #[cfg(feature = "serde")]
504 #[test]
505 fn deserialize_toml_nodelim_bdaddr_with_struct() {
506 use serde_cr::Deserialize;
507
508 #[derive(Deserialize, PartialEq, Copy, Clone, Debug)]
509 #[serde(crate = "serde_cr")]
510 struct Data {
511 #[serde(with = "crate::serde::bdaddr::no_delim")]
512 addr: BDAddr,
513 }
514
515 let data = Data {
516 addr: BDAddr::from([0xff, 0x00, 0xff, 0x00, 0xff, 0x00]),
517 };
518
519 assert_eq!(toml::from_str(r#"addr = "ff00ff00ff00""#), Ok(data));
520 assert!(
521 matches!(toml::from_str::<Data>(r"addr = 0"), Err(e) if e.message().contains("A Bluetooth address without any delimiters, like `001122334455`")),
522 );
523 }
524}