pub mod connectors;
pub mod device;
pub mod internal;
use connectors::{
ButtplugClientConnectionFuture, ButtplugClientConnector, ButtplugClientConnectorError,
};
use device::ButtplugClientDevice;
use internal::{
client_event_loop, ButtplugClientFuture, ButtplugClientMessage, ButtplugClientMessageFuture,
};
use crate::core::{
errors::{ButtplugDeviceError, ButtplugError, ButtplugHandshakeError, ButtplugMessageError},
messages::{
ButtplugMessage, ButtplugMessageUnion, DeviceMessageInfo, LogLevel, RequestDeviceList,
RequestServerInfo, StartScanning,
},
};
use async_std::{
prelude::FutureExt,
sync::{channel, Receiver, Sender},
};
use futures::{Future, StreamExt};
use std::{error::Error, fmt};
type ButtplugClientResult<T = ()> = Result<T, ButtplugClientError>;
#[derive(Debug, Clone)]
pub enum ButtplugClientError {
ButtplugClientConnectorError(ButtplugClientConnectorError),
ButtplugError(ButtplugError),
}
impl fmt::Display for ButtplugClientError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ButtplugClientError::ButtplugError(ref e) => e.fmt(f),
ButtplugClientError::ButtplugClientConnectorError(ref e) => e.fmt(f),
}
}
}
impl Error for ButtplugClientError {
fn description(&self) -> &str {
match *self {
ButtplugClientError::ButtplugError(ref e) => e.description(),
ButtplugClientError::ButtplugClientConnectorError(ref e) => e.description(),
}
}
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
impl From<ButtplugClientConnectorError> for ButtplugClientError {
fn from(error: ButtplugClientConnectorError) -> Self {
ButtplugClientError::ButtplugClientConnectorError(error)
}
}
impl From<ButtplugMessageError> for ButtplugClientError {
fn from(error: ButtplugMessageError) -> Self {
ButtplugClientError::ButtplugError(ButtplugError::ButtplugMessageError(error))
}
}
impl From<ButtplugDeviceError> for ButtplugClientError {
fn from(error: ButtplugDeviceError) -> Self {
ButtplugClientError::ButtplugError(ButtplugError::ButtplugDeviceError(error))
}
}
pub enum ButtplugClientEvent {
ScanningFinished,
DeviceAdded(ButtplugClientDevice),
DeviceRemoved(DeviceMessageInfo),
Log(LogLevel, String),
PingTimeout,
ServerDisconnect,
}
pub struct ButtplugClient {
pub client_name: String,
pub server_name: Option<String>,
message_sender: Sender<ButtplugClientMessage>,
event_receiver: Receiver<ButtplugClientEvent>,
connected: bool,
events: Vec<ButtplugClientEvent>,
}
unsafe impl Sync for ButtplugClient {}
unsafe impl Send for ButtplugClient {}
impl ButtplugClient {
pub fn run<F, T>(
name: &str,
connector: impl ButtplugClientConnector + 'static,
func: F,
) -> impl Future<Output = ButtplugClientResult>
where
F: FnOnce(ButtplugClient) -> T,
T: Future<Output = ()>,
{
debug!("Run called!");
let (event_sender, event_receiver) = channel(256);
let (message_sender, message_receiver) = channel(256);
let mut client = ButtplugClient {
client_name: name.to_string(),
server_name: None,
event_receiver,
message_sender,
connected: true,
events: vec![],
};
let app_future = async move {
client.connect(connector).await?;
func(client).await;
Ok(())
};
async move {
let internal_loop_future = client_event_loop(event_sender, message_receiver);
app_future.race(internal_loop_future).await
}
}
pub(self) async fn connect(
&mut self,
connector: impl ButtplugClientConnector + 'static,
) -> ButtplugClientResult {
debug!("Running client connection.");
let fut = ButtplugClientConnectionFuture::default();
let msg = ButtplugClientMessage::Connect(Box::new(connector), fut.get_state_clone());
self.send_internal_message(msg).await?;
debug!("Waiting on internal loop to connect");
if let Err(e) = fut.await {
return Err(ButtplugClientError::from(e));
}
info!("Client connected to server, running handshake.");
self.handshake().await
}
async fn handshake(&mut self) -> ButtplugClientResult {
info!("Running handshake with server.");
match self
.send_message(&RequestServerInfo::new(&self.client_name, 1).as_union())
.await
{
Ok(msg) => {
debug!("Got ServerInfo return.");
if let ButtplugMessageUnion::ServerInfo(server_info) = msg {
info!("Connected to {}", server_info.server_name);
self.server_name = Option::Some(server_info.server_name);
let msg = self
.send_message(&ButtplugMessageUnion::RequestDeviceList(
RequestDeviceList::default(),
))
.await?;
if let ButtplugMessageUnion::DeviceList(m) = msg {
self.send_internal_message(ButtplugClientMessage::HandleDeviceList(m))
.await?;
}
Ok(())
} else {
self.disconnect().await?;
Err(ButtplugClientError::ButtplugError(
ButtplugError::ButtplugHandshakeError(ButtplugHandshakeError {
message: "Did not receive expected ServerInfo or Error messages."
.to_string(),
}),
))
}
}
Err(e) => Err(e),
}
}
pub fn connected(&self) -> bool {
self.connected
}
pub async fn disconnect(&mut self) -> ButtplugClientResult {
let fut = ButtplugClientConnectionFuture::default();
let msg = ButtplugClientMessage::Disconnect(fut.get_state_clone());
self.send_internal_message(msg).await?;
self.connected = false;
Ok(())
}
pub async fn start_scanning(&mut self) -> ButtplugClientResult {
self.send_message_expect_ok(&ButtplugMessageUnion::StartScanning(
StartScanning::default(),
))
.await
}
async fn send_internal_message(
&mut self,
msg: ButtplugClientMessage,
) -> Result<(), ButtplugClientConnectorError> {
self.check_for_events().await?;
self.message_sender.send(msg).await;
Ok(())
}
async fn send_message(
&mut self,
msg: &ButtplugMessageUnion,
) -> Result<ButtplugMessageUnion, ButtplugClientError> {
let fut = ButtplugClientMessageFuture::default();
let internal_msg = ButtplugClientMessage::Message((msg.clone(), fut.get_state_clone()));
self.send_internal_message(internal_msg).await?;
Ok(fut.await)
}
async fn send_message_expect_ok(&mut self, msg: &ButtplugMessageUnion) -> ButtplugClientResult {
match self.send_message(msg).await? {
ButtplugMessageUnion::Ok(_) => Ok(()),
_ => Err(ButtplugClientError::from(ButtplugMessageError::new(
"Got non-Ok message back",
))),
}
}
async fn check_for_events(&mut self) -> Result<(), ButtplugClientConnectorError> {
if !self.connected {
return Err(ButtplugClientConnectorError::new("Client not connected."));
}
while !self.event_receiver.is_empty() {
match self.event_receiver.next().await {
Some(msg) => self.events.push(msg),
None => {
self.connected = false;
self.events.push(ButtplugClientEvent::ServerDisconnect);
return Err(ButtplugClientConnectorError::new("Client not connected."));
}
}
}
Ok(())
}
pub async fn wait_for_event(
&mut self,
) -> Result<ButtplugClientEvent, ButtplugClientConnectorError> {
debug!("Client waiting for event.");
if !self.connected {
return Err(ButtplugClientConnectorError::new("Client not connected."));
}
Ok({
if !self.events.is_empty() {
self.events.pop().unwrap()
} else {
match self.event_receiver.next().await {
Some(msg) => msg,
None => {
self.connected = false;
ButtplugClientEvent::ServerDisconnect
}
}
}
})
}
pub async fn devices(
&mut self,
) -> Result<Vec<ButtplugClientDevice>, ButtplugClientConnectorError> {
info!("Request devices from inner loop!");
let fut = ButtplugClientFuture::<Vec<ButtplugClientDevice>>::default();
let msg = ButtplugClientMessage::RequestDeviceList(fut.get_state_clone());
info!("Sending device request to inner loop!");
self.send_internal_message(msg).await?;
info!("Waiting for device list return!");
Ok(fut.await)
}
}
#[cfg(all(test, feature = "server"))]
mod test {
use super::ButtplugClient;
use crate::{
client::{
connectors::{
ButtplugClientConnector, ButtplugClientConnectorError,
ButtplugEmbeddedClientConnector,
},
internal::ButtplugClientMessageStateShared,
},
core::messages::ButtplugMessageUnion,
};
use async_std::{
future::Future,
sync::{channel, Receiver},
task,
};
use async_trait::async_trait;
use env_logger;
async fn connect_test_client<F, T>(func: F)
where
F: FnOnce(ButtplugClient) -> T,
T: Future<Output = ()>,
{
let _ = env_logger::builder().is_test(true).try_init();
assert!(ButtplugClient::run(
"Test Client",
ButtplugEmbeddedClientConnector::new("Test Server", 0),
func
)
.await
.is_ok());
}
#[derive(Default)]
struct ButtplugFailingConnector {}
#[async_trait]
impl ButtplugClientConnector for ButtplugFailingConnector {
async fn connect(&mut self) -> Result<(), ButtplugClientConnectorError> {
Err(ButtplugClientConnectorError::new("Always fails"))
}
async fn disconnect(&mut self) -> Result<(), ButtplugClientConnectorError> {
Err(ButtplugClientConnectorError::new("Always fails"))
}
async fn send(
&mut self,
_msg: &ButtplugMessageUnion,
_state: &ButtplugClientMessageStateShared,
) {
}
fn get_event_receiver(&mut self) -> Receiver<ButtplugMessageUnion> {
let (_send, recv) = channel(256);
recv
}
}
#[test]
fn test_failing_connection() {
let _ = env_logger::builder().is_test(true).try_init();
task::block_on(async {
assert!(ButtplugClient::run(
"Test Client",
ButtplugFailingConnector::default(),
|_| { async {} }
)
.await
.is_err());
});
}
#[test]
fn test_disconnect_status() {
task::block_on(async {
connect_test_client(|mut client| {
async move {
assert!(client.disconnect().await.is_ok());
assert!(!client.connected());
}
})
.await;
});
}
#[test]
fn test_double_disconnect() {
task::block_on(async {
connect_test_client(|mut client| {
async move {
assert!(client.disconnect().await.is_ok());
assert!(client.disconnect().await.is_err());
}
})
.await;
});
}
#[test]
fn test_connect_init() {
task::block_on(async {
connect_test_client(|client| {
async move {
assert_eq!(client.server_name.as_ref().unwrap(), "Test Server");
}
})
.await;
});
}
#[test]
fn test_start_scanning() {
task::block_on(async {
connect_test_client(|mut client| {
async move {
assert!(client.start_scanning().await.is_ok());
}
})
.await;
});
}
}