use alloc::vec::Vec;
use bstr::BStr;
use esp_idf_svc::sys;
use crate::{enums::AdvFlag, utilities::BleUuid};
pub struct BLEAdvertisedData<T>
where
T: AsRef<[u8]>,
{
payload: T,
}
impl<T: AsRef<[u8]>> BLEAdvertisedData<T> {
pub(crate) fn new(payload: T) -> Self {
Self { payload }
}
pub fn payload(&self) -> &[u8] {
self.payload.as_ref()
}
#[allow(clippy::should_implement_trait)]
pub fn clone(&self) -> BLEAdvertisedData<Vec<u8>> {
BLEAdvertisedData::<Vec<u8>>::new(self.payload().to_vec())
}
pub fn adv_flags(&self) -> Option<AdvFlag> {
let data = self
.decode()
.find(|x| x.ty == (sys::BLE_HS_ADV_TYPE_FLAGS as _))?;
let ad_flag = data.data.first()?;
AdvFlag::from_bits(*ad_flag)
}
pub fn is_advertising_service(&self, uuid: &BleUuid) -> bool {
self.service_uuids().any(|x| &x == uuid)
}
pub fn service_uuids(&self) -> impl Iterator<Item = BleUuid> + '_ {
ServiceUuidsIter {
iter: self.decode(),
current: None,
}
}
pub fn name(&self) -> Option<&BStr> {
let data = self.decode().find(|x| {
x.ty == (sys::BLE_HS_ADV_TYPE_COMP_NAME as _)
|| x.ty == (sys::BLE_HS_ADV_TYPE_INCOMP_NAME as _)
})?;
Some(BStr::new(data.data))
}
pub fn tx_power(&self) -> Option<u8> {
let data = self
.decode()
.find(|x| x.ty == (sys::BLE_HS_ADV_TYPE_TX_PWR_LVL as _))?;
data.data.first().copied()
}
pub fn service_data(&self) -> Option<BLEServiceData> {
for x in self.decode() {
match x.ty as u32 {
sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID16 => {
if let Some((uuid, service_data)) = x.data.split_at_checked(2) {
let uuid = BleUuid::from_uuid16(u16::from_le_bytes(uuid.try_into().unwrap()));
return Some(BLEServiceData { uuid, service_data });
} else {
::log::error!("Length too small for BLE_HS_ADV_TYPE_SVC_DATA_UUID16");
}
}
sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID32 => {
if let Some((uuid, service_data)) = x.data.split_at_checked(4) {
let uuid = BleUuid::from_uuid32(u32::from_le_bytes(uuid.try_into().unwrap()));
return Some(BLEServiceData { uuid, service_data });
} else {
::log::error!("Length too small for BLE_HS_ADV_TYPE_SVC_DATA_UUID32");
}
}
sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID128 => {
if let Some((uuid, service_data)) = x.data.split_at_checked(16) {
let uuid = BleUuid::from_uuid128(uuid.try_into().unwrap());
return Some(BLEServiceData { uuid, service_data });
} else {
::log::error!("Length too small for BLE_HS_ADV_TYPE_SVC_DATA_UUID128");
}
}
_ => {}
}
}
None
}
pub fn manufacture_data(&self) -> Option<ManufactureData> {
let data = self
.decode()
.find(|x| x.ty == (sys::BLE_HS_ADV_TYPE_MFG_DATA as _))?;
let (id, payload) = data.data.split_at_checked(2)?;
Some(ManufactureData {
company_identifier: u16::from_le_bytes(id.try_into().unwrap()),
payload,
})
}
fn decode(&self) -> AdStructureIter<'_> {
AdStructureIter {
payload: self.payload(),
}
}
}
impl<T: AsRef<[u8]>> core::fmt::Debug for BLEAdvertisedData<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut f = f.debug_struct("BLEAdvertisedData");
#[cfg(feature = "debug")]
f.field("types", &self.decode().map(|x| x.ty).collect::<Vec<_>>());
if let Some(adv_flags) = self.adv_flags() {
f.field("adv_flags", &adv_flags);
}
if let Some(name) = self.name() {
f.field("name", &name);
}
if let Some(tx_power) = self.tx_power() {
f.field("tx_power", &tx_power);
}
if let Some(manufacture_data) = self.manufacture_data() {
f.field("manufacture_data", &manufacture_data);
}
let service_uuids: Vec<BleUuid> = self.service_uuids().collect();
if !service_uuids.is_empty() {
f.field("service_uuids", &service_uuids);
}
if let Some(service_data) = self.service_data() {
f.field("service_data", &service_data);
}
f.finish()
}
}
#[derive(Debug, Copy, Clone)]
pub struct BLEServiceData<'a> {
pub uuid: BleUuid,
pub service_data: &'a [u8],
}
#[derive(Debug, Copy, Clone)]
pub struct ManufactureData<'a> {
pub company_identifier: u16,
pub payload: &'a [u8],
}
struct AdData<'d> {
ty: u8,
data: &'d [u8],
}
struct AdStructureIter<'d> {
payload: &'d [u8],
}
impl<'d> Iterator for AdStructureIter<'d> {
type Item = AdData<'d>;
fn next(&mut self) -> Option<Self::Item> {
let length = (*self.payload.first()?) as usize;
let (data, next_payload) = self.payload.split_at_checked(1 + length)?;
self.payload = next_payload;
if length == 0 {
return None;
}
return Some(AdData {
ty: unsafe { *data.get_unchecked(1) },
data: data.get(2..(length + 1)).unwrap(),
});
}
}
struct ServiceUuidsIter<'d> {
iter: AdStructureIter<'d>,
current: Option<AdData<'d>>,
}
impl<'d> Iterator for ServiceUuidsIter<'d> {
type Item = BleUuid;
fn next(&mut self) -> Option<Self::Item> {
if self.current.is_none() {
loop {
self.current = self.iter.next();
if let Some(current) = &self.current {
if matches!(
current.ty as u32,
sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS16
| sys::BLE_HS_ADV_TYPE_COMP_UUIDS16
| sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS32
| sys::BLE_HS_ADV_TYPE_COMP_UUIDS32
| sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS128
| sys::BLE_HS_ADV_TYPE_COMP_UUIDS128
) {
break;
}
} else {
return None;
}
}
}
let current = self.current.as_mut().unwrap();
match current.ty as u32 {
sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS16 | sys::BLE_HS_ADV_TYPE_COMP_UUIDS16 => {
if let Some((uuid, next)) = current.data.split_at_checked(2) {
current.data = next;
Some(BleUuid::from_uuid16(u16::from_le_bytes(
uuid.try_into().unwrap(),
)))
} else {
self.current = None;
self.next()
}
}
sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS32 | sys::BLE_HS_ADV_TYPE_COMP_UUIDS32 => {
if let Some((uuid, next)) = current.data.split_at_checked(4) {
current.data = next;
Some(BleUuid::from_uuid32(u32::from_le_bytes(
uuid.try_into().unwrap(),
)))
} else {
self.current = None;
self.next()
}
}
sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS128 | sys::BLE_HS_ADV_TYPE_COMP_UUIDS128 => {
if let Ok(data) = current.data.try_into() {
self.current = None;
Some(BleUuid::Uuid128(data))
} else {
self.current = None;
self.next()
}
}
_ => unreachable!(),
}
}
}