bluez 0.4.0

Control Bluetooth on Linux.
Documentation
use std::{
    fmt::{Display, Formatter},
    str::FromStr,
};

use bytes::Buf;

#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub struct Address {
    bytes: [u8; 6],
}

impl Address {
    pub const fn new(bytes: [u8; 6]) -> Address {
        Address { bytes }
    }

    pub fn from_buf<B: Buf>(buf: &mut B) -> Address {
        if buf.remaining() < 6 {
            panic!("bluetooth address is 6 bytes");
        }

        let mut arr = [0u8; 6];
        buf.copy_to_slice(&mut arr[..]);
        Address::new(arr)
    }

    pub fn from_slice(bytes: &[u8]) -> Address {
        if bytes.len() != 6 {
            panic!("bluetooth address is 6 bytes");
        }

        let mut arr = [0u8; 6];
        arr.copy_from_slice(bytes);
        Address::new(arr)
    }

    pub const fn zero() -> Address {
        Address { bytes: [0u8; 6] }
    }
}

impl From<[u8; 6]> for Address {
    fn from(bytes: [u8; 6]) -> Self {
        Address { bytes }
    }
}

impl From<Address> for [u8; 6] {
    fn from(val: Address) -> Self {
        val.bytes
    }
}

impl Into<bluez_sys::bdaddr_t> for Address {
    fn into(self) -> bluez_sys::bdaddr_t {
        bluez_sys::bdaddr_t { b: self.bytes }
    }
}

impl From<bluez_sys::bdaddr_t> for Address {
    fn from(bdaddr: bluez_sys::bdaddr_t) -> Self {
        Address { bytes: bdaddr.b }
    }
}

impl AsRef<[u8]> for Address {
    fn as_ref(&self) -> &[u8] {
        &self.bytes
    }
}

impl Display for Address {
    fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
        write!(
            f,
            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
            self.bytes[5],
            self.bytes[4],
            self.bytes[3],
            self.bytes[2],
            self.bytes[1],
            self.bytes[0]
        )
    }
}

impl FromStr for Address {
    type Err = AddressParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut it = s
            .split(':')
            .map(|octet| u8::from_str_radix(octet, 16))
            .rev();

        let bytes = [
            it.next()
                .ok_or(AddressParseError::NotEnoughOctets)?
                .or(Err(AddressParseError::InvalidOctet))?,
            it.next()
                .ok_or(AddressParseError::NotEnoughOctets)?
                .or(Err(AddressParseError::InvalidOctet))?,
            it.next()
                .ok_or(AddressParseError::NotEnoughOctets)?
                .or(Err(AddressParseError::InvalidOctet))?,
            it.next()
                .ok_or(AddressParseError::NotEnoughOctets)?
                .or(Err(AddressParseError::InvalidOctet))?,
            it.next()
                .ok_or(AddressParseError::NotEnoughOctets)?
                .or(Err(AddressParseError::InvalidOctet))?,
            it.next()
                .ok_or(AddressParseError::NotEnoughOctets)?
                .or(Err(AddressParseError::InvalidOctet))?,
        ];

        Ok(Self { bytes })
    }
}

#[derive(Error, Debug, Clone, Copy)]
pub enum AddressParseError {
    #[error("the string contained an invalid octet")]
    InvalidOctet,
    #[error("the string contained less than six octets")]
    NotEnoughOctets,
    #[error("the string contained more than six octets")]
    TooManyOctets,
}

#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive)]
pub enum AddressType {
    BREDR = 0,
    LEPublic = 1,
    LERandom = 2,
}

#[repr(u32)]
#[derive(Debug, Copy, Clone, FromPrimitive, ToPrimitive)]
pub enum Protocol {
    L2CAP = bluez_sys::BTPROTO_L2CAP,
    HCI = bluez_sys::BTPROTO_HCI,
    RFCOMM = bluez_sys::BTPROTO_RFCOMM,
}