#[cfg(feature = "bevy")]
mod bevy;
#[cfg(feature = "bevy")]
pub use bevy::*;
use web_time::Duration;
use std::{error::Error, fmt::Debug, hash::Hash};
use bytes::Bytes;
use derivative::Derivative;
use crate::lane::LaneIndex;
pub trait ClientTransport {
type Error: Error + Send + Sync;
type Connecting<'this>
where
Self: 'this;
type Connected<'this>
where
Self: 'this;
type MessageKey: Send + Sync + Debug + Clone + PartialEq + Eq + Hash;
fn state(&self) -> ClientState<Self::Connecting<'_>, Self::Connected<'_>>;
fn poll(&mut self, delta_time: Duration) -> impl Iterator<Item = ClientEvent<Self>>;
fn send(
&mut self,
msg: impl Into<Bytes>,
lane: impl Into<LaneIndex>,
) -> Result<Self::MessageKey, Self::Error>;
fn flush(&mut self) -> Result<(), Self::Error>;
fn disconnect(&mut self, reason: impl Into<String>) -> Result<(), Self::Error>;
}
#[derive(Debug, Clone, Default)]
pub enum ClientState<A, B> {
#[default]
Disconnected,
Connecting(A),
Connected(B),
}
pub type ClientStateFor<'t, T> =
ClientState<<T as ClientTransport>::Connecting<'t>, <T as ClientTransport>::Connected<'t>>;
impl<A, B> ClientState<A, B> {
pub const fn is_disconnected(&self) -> bool {
matches!(self, Self::Disconnected)
}
pub const fn is_connecting(&self) -> bool {
matches!(self, Self::Connecting(_))
}
pub const fn is_connected(&self) -> bool {
matches!(self, Self::Connected(_))
}
pub const fn as_ref(&self) -> ClientState<&A, &B> {
match self {
Self::Disconnected => ClientState::Disconnected,
Self::Connecting(a) => ClientState::Connecting(a),
Self::Connected(b) => ClientState::Connected(b),
}
}
pub fn map<A2, B2>(
self,
fa: impl FnOnce(A) -> A2,
fb: impl FnOnce(B) -> B2,
) -> ClientState<A2, B2> {
match self {
Self::Disconnected => ClientState::Disconnected,
Self::Connecting(a) => ClientState::Connecting(fa(a)),
Self::Connected(b) => ClientState::Connected(fb(b)),
}
}
}
#[derive(Derivative, PartialEq, Eq)]
#[derivative(Debug(bound = "T::Error: Debug"), Clone(bound = "T::Error: Clone"))]
pub enum ClientEvent<T: ClientTransport + ?Sized> {
Connected,
Disconnected {
reason: DisconnectReason<T::Error>,
},
Recv {
msg: Bytes,
lane: LaneIndex,
},
Ack {
msg_key: T::MessageKey,
},
Nack {
msg_key: T::MessageKey,
},
}
impl<Error, MessageKey, T> ClientEvent<T>
where
T: ClientTransport<Error = Error, MessageKey = MessageKey>,
{
pub fn remap<R>(self) -> ClientEvent<R>
where
R: ClientTransport<Error = Error, MessageKey = MessageKey>,
{
match self {
Self::Connected => ClientEvent::Connected,
Self::Disconnected { reason } => ClientEvent::Disconnected { reason },
Self::Recv { msg, lane } => ClientEvent::Recv { msg, lane },
Self::Ack { msg_key } => ClientEvent::Ack { msg_key },
Self::Nack { msg_key } => ClientEvent::Nack { msg_key },
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum DisconnectReason<E> {
#[error("disconnected locally: {0}")]
Local(String),
#[error("connection error")]
Error(
#[source]
#[from]
E,
),
#[error("disconnected remotely: {0}")]
Remote(String),
}
impl<E> DisconnectReason<E> {
pub fn map_err<F>(self, f: impl FnOnce(E) -> F) -> DisconnectReason<F> {
match self {
Self::Local(reason) => DisconnectReason::Local(reason),
Self::Error(err) => DisconnectReason::Error(f(err)),
Self::Remote(reason) => DisconnectReason::Remote(reason),
}
}
}