|  | // Copyright 2023 Google LLC | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | use crate::types::*; | 
|  |  | 
|  | use bt_common::{PeerId, Uuid}; | 
|  | use futures::{Future, Stream}; | 
|  |  | 
|  | pub enum ServiceKind { | 
|  | Primary, | 
|  | Secondary, | 
|  | } | 
|  |  | 
|  | // A short definition of a service that is either being published or has been discovered on a | 
|  | // peer. | 
|  | pub struct PeerServiceDefinition { | 
|  | /// Service Handle | 
|  | /// When publishing services, unique among all services published with [`Server::publish`]. | 
|  | pub id: u64, | 
|  | /// Whether the service is marked as Primary in the GATT server. | 
|  | pub kind: ServiceKind, | 
|  | /// The UUID identifying the type of service that this is. | 
|  | pub uuid: Uuid, | 
|  | } | 
|  |  | 
|  | /// GATT Client connected to a particular peer. | 
|  | /// Holding a struct that implements this should attempt to maintain a LE connection to the peer. | 
|  | pub trait Client { | 
|  | type PeerServiceHandleT: PeerServiceHandle; | 
|  | type ServiceResultFut: Future<Output = Result<Vec<Self::PeerServiceHandleT>>> + 'static; | 
|  |  | 
|  | /// The ID of the peer this is connected to. | 
|  | fn peer_id(&self) -> PeerId; | 
|  |  | 
|  | /// Find services by UUID on the peer. | 
|  | /// This may cause as much as a full discovery of all services on the peer if the stack deems it | 
|  | /// appropriate. | 
|  | /// Service information should be up to date at the time returned. | 
|  | fn find_service(&self, uuid: Uuid) -> Self::ServiceResultFut; | 
|  | } | 
|  |  | 
|  | pub trait PeerServiceHandle { | 
|  | type PeerServiceT: PeerService; | 
|  | type ConnectFut: Future<Output = Result<Self::PeerServiceT>>; | 
|  |  | 
|  | fn uuid(&self) -> Uuid; | 
|  | fn is_primary(&self) -> bool; | 
|  | fn connect(&self) -> Self::ConnectFut; | 
|  | } | 
|  |  | 
|  | #[derive(Debug)] | 
|  | pub struct CharacteristicNotification { | 
|  | pub handle: Handle, | 
|  | pub value: Vec<u8>, | 
|  | pub maybe_truncated: bool, | 
|  | } | 
|  |  | 
|  | /// A connection to a GATT Service on a Peer. | 
|  | /// All operations are done synchronously. | 
|  | pub trait PeerService { | 
|  | type CharacteristicsFut: Future<Output = Result<Vec<Characteristic>>>; | 
|  | type NotificationStream: Stream<Item = Result<CharacteristicNotification>>; | 
|  | type ReadFut<'a>: Future<Output = Result<(usize, bool)>> + 'a; | 
|  | type WriteFut<'a>: Future<Output = Result<()>> + 'a; | 
|  |  | 
|  | /// Discover characteristics on this service. | 
|  | /// If `uuid` is provided, only the characteristics matching `uuid` will be returned. | 
|  | /// This operation may use either the Discovery All Characteristics of a Service or | 
|  | /// Discovery Characteristic by UUID procedures, regardless of `uuid`. | 
|  | fn discover_characteristics(&self, uuid: Option<Uuid>) -> Self::CharacteristicsFut; | 
|  |  | 
|  | /// Read a characteristic into a buffer, given the handle within the service. | 
|  | /// On success, returns the size read and whether the value may have been truncated. | 
|  | /// By default this will try to use a long read if the `buf` is larger than a normal read will | 
|  | /// allow (22 bytes) or if the offset is non-zero. | 
|  | fn read_characteristic<'a>( | 
|  | &self, | 
|  | handle: &Handle, | 
|  | offset: u16, | 
|  | buf: &'a mut [u8], | 
|  | ) -> Self::ReadFut<'a>; | 
|  |  | 
|  | fn write_characteristic<'a>( | 
|  | &self, | 
|  | handle: &Handle, | 
|  | mode: WriteMode, | 
|  | offset: u16, | 
|  | buf: &'a [u8], | 
|  | ) -> Self::WriteFut<'a>; | 
|  |  | 
|  | fn read_descriptor<'a>( | 
|  | &self, | 
|  | handle: &Handle, | 
|  | offset: u16, | 
|  | buf: &'a mut [u8], | 
|  | ) -> Self::ReadFut<'a>; | 
|  |  | 
|  | fn write_descriptor<'a>( | 
|  | &self, | 
|  | handle: &Handle, | 
|  | offset: u16, | 
|  | buf: &'a [u8], | 
|  | ) -> Self::WriteFut<'a>; | 
|  |  | 
|  | /// Subscribe to updates on a Characteristic. | 
|  | /// Either notifications or indications will be enabled depending on the properties available, | 
|  | /// with indications preferred if they are supported. | 
|  | /// Fails if the Characteristic doesn't support indications or notifications. | 
|  | /// Errors are delivered through an Err item in the stream. | 
|  | /// This will often write to the Client Characteristic Configuration descriptor for the | 
|  | /// Characteristic subscribed to. | 
|  | /// Updates sent from the peer wlil be delivered to the Stream returned. | 
|  | fn subscribe(&self, handle: &Handle) -> Self::NotificationStream; | 
|  | } | 
|  |  | 
|  | /// Convenience class for communicating with characteristics on a remote peer. | 
|  | pub struct ServiceCharacteristic<'a, PeerServiceT> { | 
|  | service: &'a PeerServiceT, | 
|  | characteristic: Characteristic, | 
|  | uuid: Uuid, | 
|  | } | 
|  |  | 
|  | impl<'a, PeerServiceT: PeerService> ServiceCharacteristic<'a, PeerServiceT> { | 
|  | pub async fn find( | 
|  | service: &'a PeerServiceT, | 
|  | uuid: Uuid, | 
|  | ) -> Result<Vec<ServiceCharacteristic<'a, PeerServiceT>>> { | 
|  | let chrs = service.discover_characteristics(Some(uuid)).await?; | 
|  | Ok(chrs | 
|  | .into_iter() | 
|  | .map(|characteristic| Self { | 
|  | service, | 
|  | characteristic, | 
|  | uuid, | 
|  | }) | 
|  | .collect()) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<'a, PeerServiceT> ServiceCharacteristic<'a, PeerServiceT> { | 
|  | pub fn uuid(&self) -> Uuid { | 
|  | self.uuid | 
|  | } | 
|  |  | 
|  | pub fn handle(&self) -> &Handle { | 
|  | &self.characteristic.handle | 
|  | } | 
|  |  | 
|  | pub fn characteristic(&self) -> &Characteristic { | 
|  | &self.characteristic | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<'a, PeerServiceT: PeerService> ServiceCharacteristic<'a, PeerServiceT> { | 
|  | pub async fn read(&self, buf: &mut [u8]) -> Result<usize> { | 
|  | self.service | 
|  | .read_characteristic(self.handle(), 0, buf) | 
|  | .await | 
|  | .map(|(bytes, _)| bytes) | 
|  | } | 
|  | } |