use std::time::Duration;
use enum_iterator::Sequence;
use structbuf::{Pack, Packer, StructBuf};
use burble_const::{Service, Uuid};
use crate::gap::consts::ResponseDataType;
use crate::gap::{AdvFlag, Appearance};
use crate::hci::{ticks_1250us, ticks_625us};
use crate::le::TxPower;
#[derive(Clone, Debug)]
pub struct ResponseDataMut(StructBuf);
impl ResponseDataMut {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self(StructBuf::new(254)) }
#[allow(clippy::missing_const_for_fn)]
#[inline]
pub fn get(self) -> StructBuf {
self.0
}
pub fn service<T: Into<Uuid> + Copy>(
&mut self,
complete: bool,
uuids: impl AsRef<[T]>,
) -> &mut Self {
let uuids = uuids.as_ref();
let typ = if complete {
ResponseDataType::CompleteServiceClass16
} else {
ResponseDataType::IncompleteServiceClass16
};
self.maybe_put(complete, typ, |b| {
for v in uuids.iter().filter_map(|&u| match u.into().as_uuid16() {
None | Some(Service::GENERIC_ACCESS | Service::GENERIC_ATTRIBUTE) => None,
Some(u) => Some(u16::from(u)),
}) {
b.u16(v);
}
});
let typ = typ.next().next().flatten().expect("invalid type");
self.maybe_put(false, typ, |b| {
(uuids.iter().filter_map(|&u| u.into().as_u32())).for_each(|v| {
b.u32(v);
});
});
let typ = typ.next().next().flatten().expect("invalid type");
self.maybe_put(false, typ, |b| {
(uuids.iter().filter_map(|&u| u.into().as_u128())).for_each(|v| {
b.u128(v);
});
})
}
pub fn local_name<T: AsRef<str>>(&mut self, complete: bool, v: T) -> &mut Self {
let typ = if complete {
ResponseDataType::CompleteLocalName
} else {
ResponseDataType::ShortLocalName
};
self.put(typ, |b| {
b.put(v.as_ref().as_bytes());
})
}
pub fn flags(&mut self, v: AdvFlag) -> &mut Self {
self.put(ResponseDataType::Flags, |b| {
b.u8(v.bits());
})
}
pub fn manufacturer_data<T: AsRef<str>>(&mut self, company_id: u16, v: &[u8]) -> &mut Self {
self.put(ResponseDataType::ManufacturerData, |b| {
b.u16(company_id).put(v);
})
}
pub fn tx_power(&mut self, v: TxPower) -> &mut Self {
self.put(ResponseDataType::TxPower, |b| {
b.i8(v);
})
}
pub fn peripheral_connection_interval(
&mut self,
min: Option<Duration>,
max: Option<Duration>,
) -> &mut Self {
self.put(ResponseDataType::PeripheralConnectionIntervalRange, |b| {
b.u16(min.map_or(u16::MAX, |v| ticks_1250us(v).unwrap()));
b.u16(max.map_or(u16::MAX, |v| ticks_1250us(v).unwrap()));
})
}
pub fn appearance(&mut self, v: Appearance) -> &mut Self {
self.put(ResponseDataType::Appearance, |b| {
b.u16(v);
})
}
pub fn adv_interval(&mut self, v: Duration) -> &mut Self {
let v: [u8; 4] = ticks_625us(v).unwrap().to_le_bytes();
match v {
[v @ .., 0, 0] => self.put(ResponseDataType::AdvInterval, |b| {
b.put(v);
}),
[v @ .., 0] => self.put(ResponseDataType::AdvIntervalLong, |b| {
b.put(v);
}),
v => self.put(ResponseDataType::AdvIntervalLong, |b| {
b.put(v);
}),
}
}
#[inline]
fn put(&mut self, typ: ResponseDataType, f: impl Fn(&mut Packer)) -> &mut Self {
self.maybe_put(true, typ, f)
}
fn maybe_put(
&mut self,
keep_empty: bool,
typ: ResponseDataType,
f: impl Fn(&mut Packer),
) -> &mut Self {
let i = self.0.len();
f(self.0.append().put([0, u8::from(typ)]));
let n = u8::try_from(self.0.len().wrapping_sub(i + 1)).expect("response data overflow");
self.0[i] = n;
if !keep_empty && n < 2 {
self.0.truncate(i);
}
self
}
}
impl Default for ResponseDataMut {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use crate::sdp::ServiceClass;
use super::*;
#[test]
fn css_example_2_1_1() {
let mut eir = ResponseDataMut::new();
eir.local_name(true, "Phone").service(
true,
[ServiceClass::Panu, ServiceClass::HandsfreeAudioGateway],
);
let want = &[
0x06, 0x09, 0x50, 0x68, 0x6F, 0x6E, 0x65, 0x05, 0x03, 0x15, 0x11, 0x1F, 0x11, 0x01, 0x05, 0x01, 0x07, ];
let want = &want[..want.len() - 4]; assert_eq!(eir.get().as_ref(), want);
}
#[test]
fn css_example_2_1_2() {
let mut ad = ResponseDataMut::new();
ad.flags(AdvFlag::LE_LIMITED).local_name(true, "Pedometer");
let want = &[
0x02, 0x01, 0x01, 0x0A, 0x09, 0x50, 0x65, 0x64, 0x6F, 0x6D, 0x65, 0x74, 0x65, 0x72, ];
assert_eq!(ad.get().as_ref(), want);
}
}