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 From<BDAddr> for [u8; 6] {
80 fn from(value: BDAddr) -> Self {
81 value.address
82 }
83}
84
85impl<'a> TryFrom<&'a [u8]> for BDAddr {
86 type Error = ParseBDAddrError;
87
88 fn try_from(slice: &'a [u8]) -> Result<Self, Self::Error> {
89 Ok(Self {
90 address: slice
91 .try_into()
92 .map_err(|_| ParseBDAddrError::IncorrectByteCount)?,
93 })
94 }
95}
96
97impl TryFrom<u64> for BDAddr {
98 type Error = ParseBDAddrError;
99
100 fn try_from(int: u64) -> Result<Self, Self::Error> {
101 let slice = int.to_be_bytes(); if slice[0..2] == [0, 0] {
103 Ok(Self {
104 address: slice[2..].try_into().unwrap(),
105 })
106 } else {
107 Err(ParseBDAddrError::IncorrectByteCount)
108 }
109 }
110}
111
112impl From<BDAddr> for u64 {
113 fn from(addr: BDAddr) -> Self {
114 let mut slice = [0; 8];
115 slice[2..].copy_from_slice(&addr.into_inner());
116 u64::from_be_bytes(slice)
117 }
118}
119
120impl FromStr for BDAddr {
121 type Err = ParseBDAddrError;
122
123 fn from_str(s: &str) -> Result<Self, Self::Err> {
128 if s.contains(':') {
129 Self::from_str_delim(s)
130 } else {
131 Self::from_str_no_delim(s)
132 }
133 }
134}
135
136impl BDAddr {
137 pub fn into_inner(self) -> [u8; 6] {
139 self.address
140 }
141
142 pub fn is_random_static(&self) -> bool {
144 self.address[5] & 0b11 == 0b11
145 }
146
147 pub fn from_str_delim(s: &str) -> Result<Self, ParseBDAddrError> {
151 let bytes = s
152 .split(':')
153 .map(|part: &str| u8::from_str_radix(part, 16))
154 .collect::<Result<Vec<u8>, _>>()?;
155
156 if bytes.len() == 6 {
157 let mut address = [0; 6];
158 address.copy_from_slice(bytes.as_slice());
159 Ok(BDAddr { address })
160 } else {
161 Err(ParseBDAddrError::IncorrectByteCount)
162 }
163 }
164
165 pub fn from_str_no_delim(s: &str) -> Result<Self, ParseBDAddrError> {
169 if s.len() != 12 {
170 return Err(ParseBDAddrError::IncorrectByteCount);
171 }
172
173 let mut address = [0; 6];
174 let mut cur = s;
175 for byte in address.iter_mut() {
176 let (part, rest) = cur.split_at(2);
177 *byte = u8::from_str_radix(part, 16)?;
178 cur = rest;
179 }
180 Ok(Self { address })
181 }
182
183 pub fn write_no_delim(&self, f: &mut impl fmt::Write) -> fmt::Result {
185 for b in &self.address {
186 write!(f, "{:02x}", b)?;
187 }
188 Ok(())
189 }
190
191 pub fn to_string_no_delim(&self) -> String {
196 let mut s = String::with_capacity(12);
197 self.write_no_delim(&mut s)
198 .expect("A String-Writer never fails");
199 s
200 }
201}
202
203#[cfg(feature = "serde")]
205pub mod serde {
206 use std::fmt::{self, Write as _};
207
208 use serde::{
209 de::{Deserialize, Deserializer, Error as DeError, Visitor},
210 ser::{Serialize, Serializer},
211 };
212 use serde_cr as serde;
213
214 use super::*;
215
216 impl Serialize for BDAddr {
217 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
218 where
219 S: Serializer,
220 {
221 colon_delim::serialize(self, serializer)
222 }
223 }
224
225 impl<'de> Deserialize<'de> for BDAddr {
226 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
227 where
228 D: Deserializer<'de>,
229 {
230 colon_delim::deserialize(deserializer)
231 }
232 }
233
234 pub mod colon_delim {
257 use super::*;
258
259 struct ColonDelimVisitor;
260
261 pub fn serialize<S>(addr: &BDAddr, serializer: S) -> Result<S::Ok, S::Error>
262 where
263 S: Serializer,
264 {
265 let mut buf = String::with_capacity(17);
266 write!(&mut buf, "{:X}", addr).expect("never fails to write to string");
267 serializer.serialize_str(&buf)
268 }
269
270 pub fn deserialize<'de, D>(d: D) -> Result<BDAddr, D::Error>
271 where
272 D: Deserializer<'de>,
273 {
274 d.deserialize_str(ColonDelimVisitor)
275 }
276
277 impl<'de> Visitor<'de> for ColonDelimVisitor {
278 type Value = BDAddr;
279
280 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
281 write!(
282 formatter,
283 "A colon seperated Bluetooth address, like `00:11:22:33:44:55`"
284 )
285 }
286
287 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
288 where
289 E: DeError,
290 {
291 BDAddr::from_str_delim(v).map_err(E::custom)
292 }
293
294 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
295 where
296 E: DeError,
297 {
298 BDAddr::from_str_delim(v).map_err(E::custom)
299 }
300
301 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
302 where
303 E: DeError,
304 {
305 BDAddr::from_str_delim(&v).map_err(E::custom)
306 }
307 }
308 }
309
310 pub mod no_delim {
332 use super::*;
333
334 struct NoDelimVisitor;
335
336 pub fn serialize<S>(addr: &BDAddr, serializer: S) -> Result<S::Ok, S::Error>
337 where
338 S: Serializer,
339 {
340 let mut buf = String::with_capacity(12);
341 addr.write_no_delim(&mut buf)
342 .expect("never fails to write to string");
343 serializer.serialize_str(&buf)
344 }
345
346 pub fn deserialize<'de, D>(d: D) -> Result<BDAddr, D::Error>
347 where
348 D: Deserializer<'de>,
349 {
350 d.deserialize_str(NoDelimVisitor)
351 }
352
353 impl<'de> Visitor<'de> for NoDelimVisitor {
354 type Value = BDAddr;
355
356 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
357 write!(
358 formatter,
359 "A Bluetooth address without any delimiters, like `001122334455`"
360 )
361 }
362
363 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
364 where
365 E: DeError,
366 {
367 BDAddr::from_str_no_delim(v).map_err(E::custom)
368 }
369
370 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
371 where
372 E: DeError,
373 {
374 BDAddr::from_str_no_delim(v).map_err(E::custom)
375 }
376
377 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
378 where
379 E: DeError,
380 {
381 BDAddr::from_str_no_delim(&v).map_err(E::custom)
382 }
383 }
384 }
385
386 pub mod bytes {
408 use super::*;
409
410 pub fn serialize<S>(addr: &BDAddr, serializer: S) -> Result<S::Ok, S::Error>
411 where
412 S: Serializer,
413 {
414 addr.address.serialize(serializer)
415 }
416
417 pub fn deserialize<'de, D>(d: D) -> Result<BDAddr, D::Error>
418 where
419 D: Deserializer<'de>,
420 {
421 Ok(<[u8; 6]>::deserialize(d)?.into())
422 }
423 }
424}
425
426#[cfg(test)]
427mod tests {
428 use super::*;
429
430 const ADDR: BDAddr = BDAddr {
432 address: [0x1f, 0x2a, 0x00, 0xcc, 0x22, 0xf1],
433 };
434 const HEX: u64 = 0x00_00_1f_2a_00_cc_22_f1;
436
437 #[test]
438 fn parse_addr() {
439 let addr = BDAddr::from([0x2a, 0x00, 0xaa, 0xbb, 0xcc, 0xdd]);
440
441 let result: Result<BDAddr, _> = "2a:00:aa:bb:cc:dd".parse();
442 assert_eq!(result, Ok(addr));
443 let result: Result<BDAddr, _> = "2a00AabbCcdd".parse();
444 assert_eq!(result, Ok(addr));
445 let result: Result<BDAddr, _> = "2A:00:00".parse();
446 assert_eq!(result, Err(ParseBDAddrError::IncorrectByteCount));
447 let result: Result<BDAddr, _> = "2A:00:AA:BB:CC:ZZ".parse();
448 assert!(matches!(result, Err(ParseBDAddrError::InvalidDigit(_))));
449 let result: Result<BDAddr, _> = "2A00aABbcCZz".parse();
450 assert!(matches!(result, Err(ParseBDAddrError::InvalidDigit(_))));
451 }
452
453 #[test]
454 fn display_addr() {
455 assert_eq!(format!("{}", ADDR), "1F:2A:00:CC:22:F1");
456 assert_eq!(format!("{:?}", ADDR), "1F:2A:00:CC:22:F1");
457 assert_eq!(format!("{:x}", ADDR), "1f:2a:00:cc:22:f1");
458 assert_eq!(format!("{:X}", ADDR), "1F:2A:00:CC:22:F1");
459 assert_eq!(format!("{}", ADDR.to_string_no_delim()), "1f2a00cc22f1");
460 }
461
462 #[test]
463 fn u64_to_addr() {
464 let hex_addr: BDAddr = HEX.try_into().unwrap();
465 assert_eq!(hex_addr, ADDR);
466
467 let hex_back: u64 = hex_addr.into();
468 assert_eq!(HEX, hex_back);
469 }
470
471 #[test]
472 fn invalid_u64_to_addr() {
473 assert_eq!(
474 BDAddr::try_from(0x1122334455667788),
475 Err(ParseBDAddrError::IncorrectByteCount)
476 );
477 }
478
479 #[test]
480 fn addr_to_u64() {
481 let addr_as_hex: u64 = ADDR.into();
482 assert_eq!(HEX, addr_as_hex);
483
484 let addr_back: BDAddr = addr_as_hex.try_into().unwrap();
485 assert_eq!(ADDR, addr_back);
486 }
487
488 #[cfg(feature = "serde")]
489 #[test]
490 fn deserialize_toml_delim_bdaddr_with_struct() {
491 use serde_cr::Deserialize;
492
493 #[derive(Deserialize, PartialEq, Copy, Clone, Debug)]
494 #[serde(crate = "serde_cr")]
495 struct Data {
496 addr: BDAddr,
497 }
498
499 let data = Data {
500 addr: BDAddr::from([0xff, 0x00, 0xff, 0x00, 0xff, 0x00]),
501 };
502
503 assert_eq!(toml::from_str(r#"addr = "ff:00:ff:00:ff:00""#), Ok(data));
504 assert!(
505 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`"))
506 );
507 }
508
509 #[cfg(feature = "serde")]
510 #[test]
511 fn deserialize_toml_nodelim_bdaddr_with_struct() {
512 use serde_cr::Deserialize;
513
514 #[derive(Deserialize, PartialEq, Copy, Clone, Debug)]
515 #[serde(crate = "serde_cr")]
516 struct Data {
517 #[serde(with = "crate::serde::bdaddr::no_delim")]
518 addr: BDAddr,
519 }
520
521 let data = Data {
522 addr: BDAddr::from([0xff, 0x00, 0xff, 0x00, 0xff, 0x00]),
523 };
524
525 assert_eq!(toml::from_str(r#"addr = "ff00ff00ff00""#), Ok(data));
526 assert!(
527 matches!(toml::from_str::<Data>(r"addr = 0"), Err(e) if e.message().contains("A Bluetooth address without any delimiters, like `001122334455`")),
528 );
529 }
530}