use crate::types::DeviceId;
use tokio::sync::oneshot;
use uuid::Uuid;
#[derive(Debug, Clone, Default)]
pub struct PeripheralConfig {
pub restore_identifier: Option<String>,
}
#[derive(Debug, Clone)]
pub enum PeripheralStateEvent {
AdapterStateChanged { powered: bool },
SubscriptionChanged {
client_id: DeviceId,
char_uuid: Uuid,
subscribed: bool,
},
}
#[derive(Debug)]
pub enum PeripheralRequest {
Read {
client_id: DeviceId,
service_uuid: Uuid,
char_uuid: Uuid,
offset: u16,
responder: ReadResponder,
},
Write {
client_id: DeviceId,
service_uuid: Uuid,
char_uuid: Uuid,
value: Vec<u8>,
responder: Option<WriteResponder>,
},
}
pub(crate) type ReadResponseTx = oneshot::Sender<Result<Vec<u8>, ()>>;
pub(crate) type WriteResponseTx = oneshot::Sender<bool>;
#[derive(Debug)]
struct ResponderInner<T: Send + Clone + 'static> {
tx: Option<oneshot::Sender<T>>,
error_value: T,
}
impl<T: Send + Clone + 'static> ResponderInner<T> {
fn new(tx: oneshot::Sender<T>, error_value: T) -> Self {
Self {
tx: Some(tx),
error_value,
}
}
fn send(&mut self, value: T) {
if let Some(tx) = self.tx.take() {
let _ = tx.send(value);
}
}
}
impl<T: Send + Clone + 'static> Drop for ResponderInner<T> {
fn drop(&mut self) {
if let Some(tx) = self.tx.take() {
let _ = tx.send(self.error_value.clone());
}
}
}
#[derive(Debug)]
pub struct ReadResponder(ResponderInner<Result<Vec<u8>, ()>>);
impl ReadResponder {
pub(crate) fn new(tx: ReadResponseTx) -> Self {
Self(ResponderInner::new(tx, Err(())))
}
pub fn respond(mut self, value: Vec<u8>) {
self.0.send(Ok(value));
}
pub fn error(mut self) {
self.0.send(Err(()));
}
}
#[derive(Debug)]
pub struct WriteResponder(ResponderInner<bool>);
impl WriteResponder {
pub(crate) fn new(tx: WriteResponseTx) -> Self {
Self(ResponderInner::new(tx, false))
}
pub fn success(mut self) {
self.0.send(true);
}
pub fn error(mut self) {
self.0.send(false);
}
}
#[derive(Debug, Clone)]
pub struct AdvertisingConfig {
pub local_name: String,
pub service_uuids: Vec<Uuid>,
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_read_responder_respond() {
let (tx, rx) = oneshot::channel();
let responder = ReadResponder::new(tx);
responder.respond(b"hello".to_vec());
assert_eq!(rx.await.unwrap().unwrap(), b"hello");
}
#[tokio::test]
async fn test_write_responder_success() {
let (tx, rx) = oneshot::channel();
let responder = WriteResponder::new(tx);
responder.success();
assert!(rx.await.unwrap());
}
use proptest::collection::vec;
use proptest::prelude::*;
#[derive(Debug, Clone)]
enum ReadAction {
Drop,
Respond(Vec<u8>),
Error,
}
fn read_action() -> impl Strategy<Value = ReadAction> {
prop_oneof![
Just(ReadAction::Drop),
vec(any::<u8>(), 0..32).prop_map(ReadAction::Respond),
Just(ReadAction::Error),
]
}
#[derive(Debug, Clone)]
enum WriteAction {
Drop,
Success,
Error,
}
fn write_action() -> impl Strategy<Value = WriteAction> {
prop_oneof![
Just(WriteAction::Drop),
Just(WriteAction::Success),
Just(WriteAction::Error),
]
}
proptest! {
#[test]
fn read_responder_value_matches_action(actions in vec(read_action(), 0..64)) {
for action in actions {
let (tx, mut rx) = oneshot::channel::<Result<Vec<u8>, ()>>();
let responder = ReadResponder::new(tx);
let expected: Result<Vec<u8>, ()> = match action {
ReadAction::Drop => {
drop(responder);
Err(())
}
ReadAction::Respond(v) => {
let expected = Ok(v.clone());
responder.respond(v);
expected
}
ReadAction::Error => {
responder.error();
Err(())
}
};
let got = rx
.try_recv()
.expect("responder must deliver a value synchronously");
prop_assert_eq!(got, expected);
}
}
#[test]
fn write_responder_value_matches_action(actions in vec(write_action(), 0..64)) {
for action in actions {
let (tx, mut rx) = oneshot::channel::<bool>();
let responder = WriteResponder::new(tx);
let expected = match action {
WriteAction::Drop => {
drop(responder);
false
}
WriteAction::Success => {
responder.success();
true
}
WriteAction::Error => {
responder.error();
false
}
};
let got = rx
.try_recv()
.expect("responder must deliver a value synchronously");
prop_assert_eq!(got, expected);
}
}
}
}