Skip to main content

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 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(); // Reverse order to have MSB on index 0
102        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    /// Parses a Bluetooth address of the form `aa:bb:cc:dd:ee:ff` or of form
124    /// `aabbccddeeff`.
125    ///
126    /// All hex-digits `[0-9a-fA-F]` are allowed.
127    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    /// Destruct the address into the underlying array.
138    pub fn into_inner(self) -> [u8; 6] {
139        self.address
140    }
141
142    /// Check if this address is a randomly generated.
143    pub fn is_random_static(&self) -> bool {
144        self.address[5] & 0b11 == 0b11
145    }
146
147    /// Parses a Bluetooth address with colons `:` as delimiters.
148    ///
149    /// All hex-digits `[0-9a-fA-F]` are allowed.
150    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    /// Parses a Bluetooth address without delimiters.
166    ///
167    /// All hex-digits `[0-9a-fA-F]` are allowed.
168    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    /// Writes the address without delimiters.
184    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    /// Create a `String` with the address with no delimiters.
192    ///
193    /// For the more common presentation with colons use the `to_string()`
194    /// method.
195    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/// Different de-/serialization formats for [`BDAddr`].
204#[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    /// De-/Serialization of [`BDAddr`] as string of hex-digits with colons as delimiters.
235    ///
236    /// This is the standard used to de-/seriallize [`BDAddr`].
237    ///
238    /// # Example
239    ///
240    /// ```
241    /// # use serde_cr as serde;
242    /// use btleplug::api::BDAddr;
243    /// use serde::{Serialize, Deserialize};
244    ///
245    /// #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
246    /// # #[serde(crate = "serde_cr")]
247    /// struct S {
248    ///     addr: BDAddr,
249    /// }
250    ///
251    /// let s: S = serde_json::from_str(r#"{ "addr": "00:DE:AD:BE:EF:00" }"#)?;
252    /// let expect = S { addr: [0x00, 0xDE, 0xAD, 0xBE, 0xEF, 0x00].into() };
253    /// assert_eq!(s, expect);
254    /// # Ok::<(), Box<dyn std::error::Error>>(())
255    /// ```
256    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    /// De-/Serialization of [`BDAddr`] as string of hex-digits without any delimiters.
311    ///
312    /// # Example
313    ///
314    /// ```
315    /// # use serde_cr as serde;
316    /// use btleplug::api::BDAddr;
317    /// use serde::{Serialize, Deserialize};
318    ///
319    /// #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
320    /// # #[serde(crate = "serde_cr")]
321    /// struct S {
322    ///     #[serde(with = "btleplug::serde::bdaddr::no_delim")]
323    ///     addr: BDAddr,
324    /// }
325    ///
326    /// let s: S = serde_json::from_str(r#"{ "addr": "00deadbeef00" }"#)?;
327    /// let expect = S { addr: [0x00, 0xDE, 0xAD, 0xBE, 0xEF, 0x00].into() };
328    /// assert_eq!(s, expect);
329    /// # Ok::<(), Box<dyn std::error::Error>>(())
330    /// ```
331    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    /// De-/Serialization of [`BDAddr`] as an array of bytes.
387    ///
388    /// # Example
389    ///
390    /// ```
391    /// # use serde_cr as serde;
392    /// use btleplug::api::BDAddr;
393    /// use serde::{Serialize, Deserialize};
394    ///
395    /// #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
396    /// # #[serde(crate = "serde_cr")]
397    /// struct S {
398    ///     #[serde(with = "btleplug::serde::bdaddr::bytes")]
399    ///     addr: BDAddr,
400    /// }
401    ///
402    /// let s: S = serde_json::from_str(r#"{ "addr": [ 0, 1, 2, 3, 4, 5] }"#)?;
403    /// let expect = S { addr: [0, 1, 2, 3, 4, 5].into() };
404    /// assert_eq!(s, expect);
405    /// # Ok::<(), Box<dyn std::error::Error>>(())
406    /// ```
407    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    /// A BDAddr with the same value as `HEX`.
431    const ADDR: BDAddr = BDAddr {
432        address: [0x1f, 0x2a, 0x00, 0xcc, 0x22, 0xf1],
433    };
434    /// A u64 with the same value as `ADDR`.
435    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}