use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
mod wasm;
use wasm::WebSocketInterface;
} else {
mod native;
use native::WebSocketInterface;
}
}
pub mod error;
pub mod message;
pub mod options;
pub mod result;
pub use error::Error;
use futures::Future;
pub use message::*;
pub use options::Options;
pub use result::Result;
use async_std::channel::{Receiver, Sender};
use async_trait::async_trait;
use regex::Regex;
use std::pin::Pin;
use std::sync::Arc;
use workflow_core::channel::{oneshot, Channel};
use workflow_core::trigger::Listener;
pub type HandshakeFn = Arc<
Box<dyn Send + Sync + Fn(&Sender<Message>, &Receiver<Message>) -> HandshakeFnReturn + 'static>,
>;
pub type HandshakeFnReturn = Pin<Box<(dyn Send + Sync + 'static + Future<Output = Result<()>>)>>;
#[async_trait]
pub trait Handshake: Send + Sync + 'static {
async fn handshake(&self, sender: &Sender<Message>, receiver: &Receiver<Message>)
-> Result<()>;
}
struct Inner {
client: Arc<WebSocketInterface>,
sender_channel: Channel<(Message, Ack)>,
receiver_channel: Channel<Message>,
}
impl Inner {
pub fn new(
client: Arc<WebSocketInterface>,
sender_channel: Channel<(Message, Ack)>,
receiver_channel: Channel<Message>,
) -> Self {
Self {
client,
sender_channel,
receiver_channel,
}
}
}
#[derive(Clone)]
pub struct WebSocket {
inner: Arc<Inner>,
}
impl WebSocket {
pub fn new(url: &str, options: Options) -> Result<WebSocket> {
let schema = Regex::new(r"^wss?://").unwrap();
if !schema.is_match(url) {
return Err(Error::AddressSchema(url.to_string()));
}
let receiver_channel = if let Some(cap) = options.receiver_channel_cap {
Channel::bounded(cap)
} else {
Channel::<Message>::unbounded()
};
let sender_channel = if let Some(cap) = options.sender_channel_cap {
Channel::bounded(cap)
} else {
Channel::<(Message, Ack)>::unbounded()
};
let client = Arc::new(WebSocketInterface::new(
url,
sender_channel.clone(),
receiver_channel.clone(),
options,
)?);
let websocket = WebSocket {
inner: Arc::new(Inner::new(client, sender_channel, receiver_channel)),
};
Ok(websocket)
}
pub fn url(&self) -> String {
self.inner.client.url()
}
pub fn set_url(&self, url: &str) {
self.inner.client.set_url(url);
}
pub fn sender_tx(&self) -> &Sender<(Message, Ack)> {
&self.inner.sender_channel.sender
}
pub fn receiver_rx(&self) -> &Receiver<Message> {
&self.inner.receiver_channel.receiver
}
pub fn is_open(&self) -> bool {
self.inner.client.is_open()
}
pub async fn connect(&self, block_until_connected: bool) -> Result<Option<Listener>> {
self.inner.client.connect(block_until_connected).await
}
pub async fn disconnect(&self) -> Result<()> {
self.inner.client.disconnect().await
}
pub async fn reconnect(&self) -> Result<()> {
self.inner.client.close().await
}
pub async fn post(&self, message: Message) -> Result<&Self> {
if !self.inner.client.is_open() {
return Err(Error::NotConnected);
}
let result = Ok(self
.inner
.sender_channel
.sender
.send((message, None))
.await?);
workflow_core::task::yield_now().await;
result.map(|_| self)
}
pub async fn send(&self, message: Message) -> std::result::Result<&Self, Arc<Error>> {
if !self.inner.client.is_open() {
return Err(Arc::new(Error::NotConnected));
}
let (ack_sender, ack_receiver) = oneshot();
self.inner
.sender_channel
.send((message, Some(ack_sender)))
.await
.map_err(|err| Arc::new(err.into()))?;
ack_receiver
.recv()
.await
.map_err(|_| Arc::new(Error::DispatchChannelAck))?
.map(|_| self)
}
pub async fn recv(&self) -> Result<Message> {
Ok(self.inner.receiver_channel.receiver.recv().await?)
}
}