btleplug/api/
bdaddr.rs

1//! Implementation of Bluetooth's MAC address.
2
3use std::convert::{TryFrom, TryInto};
4use std::fmt::{self, Debug, Display, Formatter, LowerHex, UpperHex};
5use std::str::FromStr;
6
7/// Stores the 6 byte address used to identify Bluetooth devices.
8#[derive(Copy, Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub struct BDAddr {
10    address: [u8; 6],
11}
12
13/// An error parsing a [`BDAddr`] from a string.
14#[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    /// Build an address from an array.
64    ///
65    /// `address[0]` will be the MSB and `address[5]` the LSB.
66    ///
67    /// # Example
68    ///
69    /// ```
70    /// # use btleplug::api::BDAddr;
71    /// let addr: BDAddr = [0x2A, 0xCC, 0x00, 0x34, 0xFA, 0x00].into();
72    /// assert_eq!("2A:CC:00:34:FA:00", addr.to_string());
73    /// ```
74    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(); // Reverse order to have MSB on index 0
96        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    /// Parses a Bluetooth address of the form `aa:bb:cc:dd:ee:ff` or of form
118    /// `aabbccddeeff`.
119    ///
120    /// All hex-digits `[0-9a-fA-F]` are allowed.
121    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    /// Destruct the address into the underlying array.
132    pub fn into_inner(self) -> [u8; 6] {
133        self.address
134    }
135
136    /// Check if this address is a randomly generated.
137    pub fn is_random_static(&self) -> bool {
138        self.address[5] & 0b11 == 0b11
139    }
140
141    /// Parses a Bluetooth address with colons `:` as delimiters.
142    ///
143    /// All hex-digits `[0-9a-fA-F]` are allowed.
144    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    /// Parses a Bluetooth address without delimiters.
160    ///
161    /// All hex-digits `[0-9a-fA-F]` are allowed.
162    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    /// Writes the address without delimiters.
178    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    /// Create a `String` with the address with no delimiters.
186    ///
187    /// For the more common presentation with colons use the `to_string()`
188    /// method.
189    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/// Different de-/serialization formats for [`BDAddr`].
198#[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    /// De-/Serialization of [`BDAddr`] as string of hex-digits with colons as delimiters.
229    ///
230    /// This is the standard used to de-/seriallize [`BDAddr`].
231    ///
232    /// # Example
233    ///
234    /// ```
235    /// # use serde_cr as serde;
236    /// use btleplug::api::BDAddr;
237    /// use serde::{Serialize, Deserialize};
238    ///
239    /// #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
240    /// # #[serde(crate = "serde_cr")]
241    /// struct S {
242    ///     addr: BDAddr,
243    /// }
244    ///
245    /// let s: S = serde_json::from_str(r#"{ "addr": "00:DE:AD:BE:EF:00" }"#)?;
246    /// let expect = S { addr: [0x00, 0xDE, 0xAD, 0xBE, 0xEF, 0x00].into() };
247    /// assert_eq!(s, expect);
248    /// # Ok::<(), Box<dyn std::error::Error>>(())
249    /// ```
250    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    /// De-/Serialization of [`BDAddr`] as string of hex-digits without any delimiters.
305    ///
306    /// # Example
307    ///
308    /// ```
309    /// # use serde_cr as serde;
310    /// use btleplug::api::BDAddr;
311    /// use serde::{Serialize, Deserialize};
312    ///
313    /// #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
314    /// # #[serde(crate = "serde_cr")]
315    /// struct S {
316    ///     #[serde(with = "btleplug::serde::bdaddr::no_delim")]
317    ///     addr: BDAddr,
318    /// }
319    ///
320    /// let s: S = serde_json::from_str(r#"{ "addr": "00deadbeef00" }"#)?;
321    /// let expect = S { addr: [0x00, 0xDE, 0xAD, 0xBE, 0xEF, 0x00].into() };
322    /// assert_eq!(s, expect);
323    /// # Ok::<(), Box<dyn std::error::Error>>(())
324    /// ```
325    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    /// De-/Serialization of [`BDAddr`] as an array of bytes.
381    ///
382    /// # Example
383    ///
384    /// ```
385    /// # use serde_cr as serde;
386    /// use btleplug::api::BDAddr;
387    /// use serde::{Serialize, Deserialize};
388    ///
389    /// #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
390    /// # #[serde(crate = "serde_cr")]
391    /// struct S {
392    ///     #[serde(with = "btleplug::serde::bdaddr::bytes")]
393    ///     addr: BDAddr,
394    /// }
395    ///
396    /// let s: S = serde_json::from_str(r#"{ "addr": [ 0, 1, 2, 3, 4, 5] }"#)?;
397    /// let expect = S { addr: [0, 1, 2, 3, 4, 5].into() };
398    /// assert_eq!(s, expect);
399    /// # Ok::<(), Box<dyn std::error::Error>>(())
400    /// ```
401    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    /// A BDAddr with the same value as `HEX`.
425    const ADDR: BDAddr = BDAddr {
426        address: [0x1f, 0x2a, 0x00, 0xcc, 0x22, 0xf1],
427    };
428    /// A u64 with the same value as `ADDR`.
429    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}