#![allow(clippy::let_unit_value)]
use objc_foundation::{INSArray, INSFastEnumeration, INSString, NSArray};
use objc_id::ShareId;
use super::delegates::{PeripheralDelegate, PeripheralEvent};
use super::types::{CBPeripheral, CBPeripheralState, CBUUID};
use crate::error::ErrorKind;
use crate::pairing::PairingAgent;
use crate::{Device, DeviceId, Error, Result, Service, Uuid};
#[derive(Clone)]
pub struct DeviceImpl {
pub(super) peripheral: ShareId<CBPeripheral>,
delegate: ShareId<PeripheralDelegate>,
}
impl PartialEq for DeviceImpl {
fn eq(&self, other: &Self) -> bool {
self.peripheral == other.peripheral
}
}
impl Eq for DeviceImpl {}
impl std::hash::Hash for DeviceImpl {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.peripheral.hash(state);
}
}
impl std::fmt::Debug for DeviceImpl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Device").field(&self.peripheral).finish()
}
}
impl std::fmt::Display for DeviceImpl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.name().as_deref().unwrap_or("(Unknown)"))
}
}
impl Device {
pub(super) fn new(peripheral: ShareId<CBPeripheral>) -> Self {
let delegate = peripheral.delegate().unwrap_or_else(|| {
let (sender, _) = tokio::sync::broadcast::channel(16);
let delegate = PeripheralDelegate::with_sender(sender).share();
peripheral.set_delegate(&delegate);
delegate
});
Device(DeviceImpl { peripheral, delegate })
}
}
impl DeviceImpl {
pub fn id(&self) -> DeviceId {
super::DeviceId(self.peripheral.identifier().to_uuid())
}
pub fn name(&self) -> Result<String> {
match self.peripheral.name() {
Some(name) => Ok(name.as_str().to_owned()),
None => Err(ErrorKind::NotFound.into()),
}
}
pub async fn name_async(&self) -> Result<String> {
self.name()
}
pub async fn is_connected(&self) -> bool {
self.peripheral.state() == CBPeripheralState::CONNECTED
}
pub async fn is_paired(&self) -> Result<bool> {
Err(ErrorKind::NotSupported.into())
}
pub async fn pair(&self) -> Result<()> {
Ok(())
}
pub async fn pair_with_agent<T: PairingAgent>(&self, _agent: &T) -> Result<()> {
Ok(())
}
pub async fn unpair(&self) -> Result<()> {
Err(ErrorKind::NotSupported.into())
}
pub async fn discover_services(&self) -> Result<Vec<Service>> {
self.discover_services_inner(None).await
}
pub async fn discover_services_with_uuid(&self, uuid: Uuid) -> Result<Vec<Service>> {
let uuids = {
let vec = vec![CBUUID::from_uuid(uuid)];
NSArray::from_vec(vec)
};
let services = self.discover_services_inner(Some(&uuids)).await?;
Ok(services.into_iter().filter(|x| x.uuid() == uuid).collect())
}
async fn discover_services_inner(&self, uuids: Option<&NSArray<CBUUID>>) -> Result<Vec<Service>> {
let mut receiver = self.delegate.sender().subscribe();
if !self.is_connected().await {
return Err(ErrorKind::NotConnected.into());
}
self.peripheral.discover_services(uuids);
loop {
match receiver.recv().await.map_err(Error::from_recv_error)? {
PeripheralEvent::DiscoveredServices { error: None } => break,
PeripheralEvent::DiscoveredServices { error: Some(err) } => return Err(Error::from_nserror(err)),
PeripheralEvent::Disconnected { error } => {
return Err(Error::from_kind_and_nserror(ErrorKind::NotConnected, error));
}
_ => (),
}
}
self.services().await
}
pub async fn services(&self) -> Result<Vec<Service>> {
self.peripheral
.services()
.map(|s| s.enumerator().map(Service::new).collect())
.ok_or_else(|| {
Error::new(
ErrorKind::NotReady,
None,
"no services have been discovered".to_string(),
)
})
}
pub async fn services_changed(&self) -> Result<()> {
let mut receiver = self.delegate.sender().subscribe();
if !self.is_connected().await {
return Err(ErrorKind::NotConnected.into());
}
loop {
match receiver.recv().await.map_err(Error::from_recv_error)? {
PeripheralEvent::ServicesChanged { .. } => break,
PeripheralEvent::Disconnected { error } => {
return Err(Error::from_kind_and_nserror(ErrorKind::NotConnected, error))
}
_ => (),
}
}
Ok(())
}
pub async fn rssi(&self) -> Result<i16> {
let mut receiver = self.delegate.sender().subscribe();
self.peripheral.read_rssi();
loop {
match receiver.recv().await {
Ok(PeripheralEvent::ReadRssi { rssi, error: None }) => return Ok(rssi),
Ok(PeripheralEvent::ReadRssi { error: Some(err), .. }) => return Err(Error::from_nserror(err)),
Err(err) => return Err(Error::from_recv_error(err)),
_ => (),
}
}
}
}