Buttplug sex toy control library
at dev 4.0 kB view raw
1// Buttplug Rust Source Code File - See https://buttplug.io for more info. 2// 3// Copyright 2016-2024 Nonpolynomial Labs LLC. All rights reserved. 4// 5// Licensed under the BSD 3-Clause license. See LICENSE file in the project root 6// for full license information. 7 8use crate::device::hardware::HardwareConnector; 9use async_trait::async_trait; 10use buttplug_core::{ 11 util::{async_manager, sleep}, 12 {ButtplugResultFuture, errors::ButtplugDeviceError}, 13}; 14use futures::future::{self, FutureExt}; 15use serde::{Deserialize, Serialize}; 16use std::{sync::Arc, time::Duration}; 17use thiserror::Error; 18use tokio::sync::mpsc::Sender; 19use tokio_util::sync::CancellationToken; 20 21#[derive(Debug)] 22pub enum HardwareCommunicationManagerEvent { 23 // This event only means that a device has been found. The work still needs 24 // to be done to make sure we can use it. 25 DeviceFound { 26 name: String, 27 address: String, 28 creator: Box<dyn HardwareConnector>, 29 }, 30 ScanningFinished, 31} 32 33pub trait HardwareCommunicationManagerBuilder: Send { 34 fn finish( 35 &mut self, 36 sender: Sender<HardwareCommunicationManagerEvent>, 37 ) -> Box<dyn HardwareCommunicationManager>; 38} 39 40pub trait HardwareCommunicationManager: Send + Sync { 41 fn name(&self) -> &'static str; 42 fn start_scanning(&mut self) -> ButtplugResultFuture; 43 fn stop_scanning(&mut self) -> ButtplugResultFuture; 44 fn scanning_status(&self) -> bool { 45 false 46 } 47 fn can_scan(&self) -> bool; 48 // Events happen via channel senders passed to the comm manager. 49} 50 51#[derive(Error, Debug, Clone, Display, Serialize, Deserialize, PartialEq, Eq)] 52pub enum HardwareSpecificError { 53 // HardwareSpecificError: {} Error: {} 54 HardwareSpecificError(String, String), 55} 56 57#[async_trait] 58pub trait TimedRetryCommunicationManagerImpl: Sync + Send { 59 fn name(&self) -> &'static str; 60 fn can_scan(&self) -> bool; 61 fn rescan_wait_duration(&self) -> Duration { 62 Duration::from_secs(1) 63 } 64 async fn scan(&self) -> Result<(), ButtplugDeviceError>; 65} 66 67pub struct TimedRetryCommunicationManager<T: TimedRetryCommunicationManagerImpl + 'static> { 68 comm_manager: Arc<T>, 69 cancellation_token: Option<CancellationToken>, 70} 71 72impl<T: TimedRetryCommunicationManagerImpl> TimedRetryCommunicationManager<T> { 73 pub fn new(comm_manager: T) -> Self { 74 Self { 75 comm_manager: Arc::new(comm_manager), 76 cancellation_token: None, 77 } 78 } 79} 80 81impl<T: TimedRetryCommunicationManagerImpl> HardwareCommunicationManager 82 for TimedRetryCommunicationManager<T> 83{ 84 fn name(&self) -> &'static str { 85 self.comm_manager.name() 86 } 87 88 fn start_scanning(&mut self) -> ButtplugResultFuture { 89 if self.cancellation_token.is_some() { 90 return future::ready(Ok(())).boxed(); 91 } 92 let comm_manager = self.comm_manager.clone(); 93 let token = CancellationToken::new(); 94 let child_token = token.child_token(); 95 self.cancellation_token = Some(token); 96 let duration = self.comm_manager.rescan_wait_duration(); 97 async move { 98 async_manager::spawn(async move { 99 loop { 100 if let Err(err) = comm_manager.scan().await { 101 error!("Timed Device Communication Manager Failure: {}", err); 102 break; 103 } 104 tokio::select! { 105 _ = sleep(duration) => continue, 106 _ = child_token.cancelled() => break, 107 } 108 } 109 }); 110 Ok(()) 111 } 112 .boxed() 113 } 114 115 fn stop_scanning(&mut self) -> ButtplugResultFuture { 116 if self.cancellation_token.is_none() { 117 return future::ready(Ok(())).boxed(); 118 } 119 self.cancellation_token.take().unwrap().cancel(); 120 future::ready(Ok(())).boxed() 121 } 122 123 fn scanning_status(&self) -> bool { 124 self.cancellation_token.is_some() 125 } 126 fn can_scan(&self) -> bool { 127 self.comm_manager.can_scan() 128 } 129} 130 131impl<T: TimedRetryCommunicationManagerImpl> Drop for TimedRetryCommunicationManager<T> { 132 fn drop(&mut self) { 133 // We set the cancellation token without doing anything with the future, so we're fine to ignore 134 // the return. 135 let _ = self.stop_scanning(); 136 } 137}