pub use wacore::net::{DisconnectReason, Transport, TransportEvent, TransportFactory};
#[cfg(feature = "tokio-transport")]
pub use whatsapp_rust_tokio_transport::{
Connector, TokioWebSocketTransportFactory, default_tls_connector, from_websocket,
};
#[cfg(test)]
pub mod mock {
use super::*;
use async_trait::async_trait;
use std::sync::Arc;
pub struct MockTransport;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl Transport for MockTransport {
async fn send(&self, _data: bytes::Bytes) -> Result<(), anyhow::Error> {
Ok(())
}
async fn disconnect(&self) {}
}
#[derive(Default)]
pub struct MockTransportFactory;
impl MockTransportFactory {
pub fn new() -> Self {
Self
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl TransportFactory for MockTransportFactory {
async fn create_transport(
&self,
) -> Result<(Arc<dyn Transport>, async_channel::Receiver<TransportEvent>), anyhow::Error>
{
let (_tx, rx) = async_channel::bounded(1);
Ok((Arc::new(MockTransport), rx))
}
}
fn split_framed(write: &bytes::Bytes) -> Vec<bytes::Bytes> {
const PREFIX: usize = 3;
let mut frames = Vec::new();
let mut offset = 0usize;
while offset + PREFIX <= write.len() {
let len = ((write[offset] as usize) << 16)
| ((write[offset + 1] as usize) << 8)
| (write[offset + 2] as usize);
let end = offset + PREFIX + len;
if end > write.len() {
break;
}
frames.push(write.slice(offset..end));
offset = end;
}
if offset < write.len() {
frames.push(write.slice(offset..));
}
frames
}
pub struct CapturingMockTransport {
sent: std::sync::Mutex<Vec<bytes::Bytes>>,
remaining_failures: std::sync::atomic::AtomicUsize,
failed_sends: std::sync::atomic::AtomicUsize,
}
impl CapturingMockTransport {
pub fn new() -> Self {
Self {
sent: std::sync::Mutex::new(Vec::new()),
remaining_failures: std::sync::atomic::AtomicUsize::new(0),
failed_sends: std::sync::atomic::AtomicUsize::new(0),
}
}
pub fn sent(&self) -> Vec<bytes::Bytes> {
self.sent_writes().iter().flat_map(split_framed).collect()
}
pub fn sent_writes(&self) -> Vec<bytes::Bytes> {
self.sent.lock().expect("capturing mutex").clone()
}
pub fn sent_count(&self) -> usize {
self.sent().len()
}
pub fn write_count(&self) -> usize {
self.sent.lock().expect("capturing mutex").len()
}
pub fn fail_next_sends(&self, count: usize) {
self.remaining_failures
.store(count, std::sync::atomic::Ordering::Release);
}
pub fn failed_sends(&self) -> usize {
self.failed_sends.load(std::sync::atomic::Ordering::Acquire)
}
}
impl Default for CapturingMockTransport {
fn default() -> Self {
Self::new()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl Transport for CapturingMockTransport {
async fn send(&self, data: bytes::Bytes) -> Result<(), anyhow::Error> {
if self
.remaining_failures
.fetch_update(
std::sync::atomic::Ordering::AcqRel,
std::sync::atomic::Ordering::Acquire,
|remaining| remaining.checked_sub(1),
)
.is_ok()
{
self.failed_sends
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
return Err(anyhow::anyhow!("injected transport failure"));
}
self.sent.lock().expect("capturing mutex").push(data);
Ok(())
}
async fn disconnect(&self) {}
}
pub struct CapturingMockTransportFactory {
transport: Arc<CapturingMockTransport>,
}
impl CapturingMockTransportFactory {
pub fn new() -> Self {
Self {
transport: Arc::new(CapturingMockTransport::new()),
}
}
pub fn transport(&self) -> Arc<CapturingMockTransport> {
self.transport.clone()
}
}
impl Default for CapturingMockTransportFactory {
fn default() -> Self {
Self::new()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl TransportFactory for CapturingMockTransportFactory {
async fn create_transport(
&self,
) -> Result<(Arc<dyn Transport>, async_channel::Receiver<TransportEvent>), anyhow::Error>
{
let (_tx, rx) = async_channel::bounded(1);
Ok((self.transport.clone(), rx))
}
}
}