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            let buf = d.deserialize_str(ColonDelimVisitor)?;
269            BDAddr::from_str_delim(buf).map_err(D::Error::custom)
270        }
271
272        impl<'de> Visitor<'de> for ColonDelimVisitor {
273            type Value = &'de str;
274
275            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
276                write!(
277                    formatter,
278                    "A colon seperated Bluetooth address, like `00:11:22:33:44:55`"
279                )
280            }
281
282            fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
283            where
284                E: DeError,
285            {
286                Ok(v)
287            }
288        }
289    }
290
291    /// De-/Serialization of [`BDAddr`] as string of hex-digits without any delimiters.
292    ///
293    /// # Example
294    ///
295    /// ```
296    /// # use serde_cr as serde;
297    /// use btleplug::api::BDAddr;
298    /// use serde::{Serialize, Deserialize};
299    ///
300    /// #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
301    /// # #[serde(crate = "serde_cr")]
302    /// struct S {
303    ///     #[serde(with = "btleplug::serde::bdaddr::no_delim")]
304    ///     addr: BDAddr,
305    /// }
306    ///
307    /// let s: S = serde_json::from_str(r#"{ "addr": "00deadbeef00" }"#)?;
308    /// let expect = S { addr: [0x00, 0xDE, 0xAD, 0xBE, 0xEF, 0x00].into() };
309    /// assert_eq!(s, expect);
310    /// # Ok::<(), Box<dyn std::error::Error>>(())
311    /// ```
312    pub mod no_delim {
313        use super::*;
314
315        struct NoDelimVisitor;
316
317        pub fn serialize<S>(addr: &BDAddr, serializer: S) -> Result<S::Ok, S::Error>
318        where
319            S: Serializer,
320        {
321            let mut buf = String::with_capacity(12);
322            addr.write_no_delim(&mut buf)
323                .expect("never fails to write to string");
324            serializer.serialize_str(&buf)
325        }
326
327        pub fn deserialize<'de, D>(d: D) -> Result<BDAddr, D::Error>
328        where
329            D: Deserializer<'de>,
330        {
331            let buf = d.deserialize_str(NoDelimVisitor)?;
332            BDAddr::from_str_no_delim(buf).map_err(D::Error::custom)
333        }
334
335        impl<'de> Visitor<'de> for NoDelimVisitor {
336            type Value = &'de str;
337
338            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
339                write!(
340                    formatter,
341                    "A Bluetooth address without any delimiters, like `001122334455`"
342                )
343            }
344
345            fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
346            where
347                E: DeError,
348            {
349                Ok(v)
350            }
351        }
352    }
353
354    /// De-/Serialization of [`BDAddr`] as an array of bytes.
355    ///
356    /// # Example
357    ///
358    /// ```
359    /// # use serde_cr as serde;
360    /// use btleplug::api::BDAddr;
361    /// use serde::{Serialize, Deserialize};
362    ///
363    /// #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
364    /// # #[serde(crate = "serde_cr")]
365    /// struct S {
366    ///     #[serde(with = "btleplug::serde::bdaddr::bytes")]
367    ///     addr: BDAddr,
368    /// }
369    ///
370    /// let s: S = serde_json::from_str(r#"{ "addr": [ 0, 1, 2, 3, 4, 5] }"#)?;
371    /// let expect = S { addr: [0, 1, 2, 3, 4, 5].into() };
372    /// assert_eq!(s, expect);
373    /// # Ok::<(), Box<dyn std::error::Error>>(())
374    /// ```
375    pub mod bytes {
376        use super::*;
377
378        pub fn serialize<S>(addr: &BDAddr, serializer: S) -> Result<S::Ok, S::Error>
379        where
380            S: Serializer,
381        {
382            addr.address.serialize(serializer)
383        }
384
385        pub fn deserialize<'de, D>(d: D) -> Result<BDAddr, D::Error>
386        where
387            D: Deserializer<'de>,
388        {
389            Ok(<[u8; 6]>::deserialize(d)?.into())
390        }
391    }
392}
393
394#[cfg(test)]
395mod tests {
396    use super::*;
397
398    /// A BDAddr with the same value as `HEX`.
399    const ADDR: BDAddr = BDAddr {
400        address: [0x1f, 0x2a, 0x00, 0xcc, 0x22, 0xf1],
401    };
402    /// A u64 with the same value as `ADDR`.
403    const HEX: u64 = 0x00_00_1f_2a_00_cc_22_f1;
404
405    #[test]
406    fn parse_addr() {
407        let addr = BDAddr::from([0x2a, 0x00, 0xaa, 0xbb, 0xcc, 0xdd]);
408
409        let result: Result<BDAddr, _> = "2a:00:aa:bb:cc:dd".parse();
410        assert_eq!(result, Ok(addr));
411        let result: Result<BDAddr, _> = "2a00AabbCcdd".parse();
412        assert_eq!(result, Ok(addr));
413        let result: Result<BDAddr, _> = "2A:00:00".parse();
414        assert_eq!(result, Err(ParseBDAddrError::IncorrectByteCount));
415        let result: Result<BDAddr, _> = "2A:00:AA:BB:CC:ZZ".parse();
416        assert!(matches!(result, Err(ParseBDAddrError::InvalidDigit(_))));
417        let result: Result<BDAddr, _> = "2A00aABbcCZz".parse();
418        assert!(matches!(result, Err(ParseBDAddrError::InvalidDigit(_))));
419    }
420
421    #[test]
422    fn display_addr() {
423        assert_eq!(format!("{}", ADDR), "1F:2A:00:CC:22:F1");
424        assert_eq!(format!("{:?}", ADDR), "1F:2A:00:CC:22:F1");
425        assert_eq!(format!("{:x}", ADDR), "1f:2a:00:cc:22:f1");
426        assert_eq!(format!("{:X}", ADDR), "1F:2A:00:CC:22:F1");
427        assert_eq!(format!("{}", ADDR.to_string_no_delim()), "1f2a00cc22f1");
428    }
429
430    #[test]
431    fn u64_to_addr() {
432        let hex_addr: BDAddr = HEX.try_into().unwrap();
433        assert_eq!(hex_addr, ADDR);
434
435        let hex_back: u64 = hex_addr.into();
436        assert_eq!(HEX, hex_back);
437    }
438
439    #[test]
440    fn invalid_u64_to_addr() {
441        assert_eq!(
442            BDAddr::try_from(0x1122334455667788),
443            Err(ParseBDAddrError::IncorrectByteCount)
444        );
445    }
446
447    #[test]
448    fn addr_to_u64() {
449        let addr_as_hex: u64 = ADDR.into();
450        assert_eq!(HEX, addr_as_hex);
451
452        let addr_back: BDAddr = addr_as_hex.try_into().unwrap();
453        assert_eq!(ADDR, addr_back);
454    }
455}