use crate::{
gatt_server::characteristic::Characteristic, gatt_server::descriptor::Descriptor,
leaky_box_raw, utilities::BleUuid,
};
use esp_idf_sys::*;
use log::debug;
use std::{
fmt::Formatter,
sync::{Arc, RwLock},
};
#[derive(Debug, Clone)]
pub struct Service {
name: Option<String>,
pub(crate) uuid: BleUuid,
pub(crate) characteristics: Vec<Arc<RwLock<Characteristic>>>,
primary: bool,
pub(crate) handle: Option<u16>,
}
impl Service {
#[must_use]
pub const fn new(uuid: BleUuid) -> Self {
Self {
name: None,
uuid,
characteristics: Vec::new(),
primary: false,
handle: None,
}
}
pub fn name<S: Into<String>>(&mut self, name: S) -> &mut Self {
self.name = Some(name.into());
self
}
pub fn primary(&mut self) -> &mut Self {
self.primary = true;
self
}
pub fn characteristic(&mut self, characteristic: &Arc<RwLock<Characteristic>>) -> &mut Self {
self.characteristics.push(characteristic.clone());
self
}
#[must_use]
pub fn build(&self) -> Arc<RwLock<Self>> {
Arc::new(RwLock::new(self.clone()))
}
pub(crate) fn get_characteristic_by_handle(
&self,
handle: u16,
) -> Option<Arc<RwLock<Characteristic>>> {
self.characteristics
.iter()
.find(|characteristic| characteristic.read().unwrap().attribute_handle == Some(handle))
.cloned()
}
pub(crate) fn get_characteristic_by_id(
&self,
id: esp_bt_uuid_t,
) -> Option<Arc<RwLock<Characteristic>>> {
self.characteristics
.iter()
.find(|characteristic| characteristic.read().unwrap().uuid == id.into())
.cloned()
}
pub(crate) fn get_descriptors_by_id(&self, id: esp_bt_uuid_t) -> Vec<Arc<RwLock<Descriptor>>> {
self.characteristics
.iter()
.filter_map(|characteristic| {
characteristic
.read()
.unwrap()
.clone()
.descriptors
.into_iter()
.find(|descriptor| descriptor.read().unwrap().uuid == id.into())
})
.collect()
}
pub(crate) fn register_self(&mut self, interface: u8) {
debug!("Registering {} on interface {}.", &self, interface);
let id: esp_gatt_srvc_id_t = esp_gatt_srvc_id_t {
id: self.uuid.into(),
is_primary: self.primary,
};
unsafe {
esp_nofail!(esp_ble_gatts_create_service(
interface,
leaky_box_raw!(id),
256, ));
}
}
pub(crate) fn register_characteristics(&mut self) {
debug!("Registering {}'s characteristics.", &self);
if self.characteristics.is_empty() {
return;
}
let service_handle = self.handle.unwrap();
let characteristics = self.characteristics.clone();
std::thread::spawn(move || {
for c in characteristics {
c.write().unwrap().register_self(service_handle);
while c.read().unwrap().attribute_handle.is_none() {
std::thread::yield_now();
}
}
});
}
}
impl std::fmt::Display for Service {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} ({})",
self.name
.clone()
.unwrap_or_else(|| "Unnamed service".to_string()),
self.uuid,
)
}
}