#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(feature = "logging")]
pub mod logging;
#[cfg(feature = "logging")]
pub use logging::{Logger, Logging};
use std::{
fmt::Display,
io::{Read, Write},
pin::Pin,
task::{Context, Poll},
};
use futures::{
future::FusedFuture,
ready,
stream::{FusedStream, Next},
Future, FutureExt, Sink, Stream, StreamExt,
};
use pin_project::pin_project;
use serde::{Deserialize, Serialize};
pub trait Encode {
type Error: std::error::Error;
fn encode<W, T>(&mut self, writer: W, message: &T) -> Result<(), Self::Error>
where
W: Write,
T: Serialize;
}
pub trait Decode {
type Error: std::error::Error;
fn decode<R, T>(&mut self, data: R) -> Result<T, Self::Error>
where
R: Read,
for<'de> T: Deserialize<'de>;
}
pub trait Codec: Encode + Decode {}
impl<T> Codec for T where T: Encode + Decode {}
#[derive(Debug, PartialEq, Eq)]
pub enum Error<Other> {
Closed,
Other(Other),
}
impl<Other> Error<Other> {
pub fn closed(&self) -> bool {
matches!(self, Error::Closed)
}
}
impl<Other> Display for Error<Other>
where
Other: Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Closed => write!(f, "transport closed"),
Self::Other(other) => write!(f, "{other}"),
}
}
}
impl<Other> std::error::Error for Error<Other> where Other: std::error::Error {}
pub trait Receive<Message, Error> {
fn receive(&mut self) -> Recv<'_, Self>;
}
impl<T, Message, Error> Receive<Message, Error> for T
where
T: Stream<Item = Result<Message, Error>> + Unpin,
{
fn receive(&mut self) -> Recv<'_, Self> {
let next = self.next();
Recv {
next,
terminated: false,
}
}
}
pub struct Recv<'a, T>
where
T: ?Sized,
{
next: Next<'a, T>,
terminated: bool,
}
impl<T, Message, Error> Future for Recv<'_, T>
where
T: Stream<Item = Result<Message, Error>> + Unpin,
{
type Output = Result<Message, self::Error<Error>>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.next.poll_unpin(cx) {
Poll::Ready(item) => {
self.terminated = true;
if let Some(item) = item {
Poll::Ready(item.map_err(|error| self::Error::Other(error)))
} else {
Poll::Ready(Err(self::Error::Closed))
}
}
Poll::Pending => Poll::Pending,
}
}
}
impl<T, Message, Error> FusedFuture for Recv<'_, T>
where
T: Stream<Item = Result<Message, Error>> + Unpin,
{
fn is_terminated(&self) -> bool {
self.terminated
}
}
pub trait Messages<T, Message, Error>
where
Self: Sized,
{
fn messages_with_error_callback<F>(self, error_callback: F) -> MessageStream<Self, F>
where
F: FnMut(Error);
fn messages(self) -> MessageStream<Self, fn(Error) -> ()> {
self.messages_with_error_callback(|_| {})
}
}
impl<T, Message, Error> Messages<T, Message, Error> for T
where
T: Stream<Item = Result<Message, Error>> + Unpin,
{
fn messages_with_error_callback<F>(self, error_callback: F) -> MessageStream<Self, F>
where
F: FnMut(Error),
{
MessageStream {
stream: self,
error_callback,
terminated: false,
}
}
}
#[pin_project]
pub struct MessageStream<T, F> {
#[pin]
stream: T,
error_callback: F,
terminated: bool,
}
impl<T, F, Message, Error> Stream for MessageStream<T, F>
where
T: Stream<Item = Result<Message, Error>> + Unpin,
F: FnMut(Error),
{
type Item = Message;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
loop {
if let Some(result) = ready!(self.stream.poll_next_unpin(cx)) {
match result {
Ok(message) => return Poll::Ready(Some(message)),
Err(error) => {
(self.error_callback)(error);
continue;
}
}
} else {
self.terminated = true;
return Poll::Ready(None);
}
}
}
}
impl<T, F, Message, Error> FusedStream for MessageStream<T, F>
where
T: Stream<Item = Result<Message, Error>> + Unpin,
F: FnMut(Error),
{
fn is_terminated(&self) -> bool {
self.terminated
}
}
#[doc(hidden)]
pub mod conditional {
#[cfg(feature = "logging")]
pub trait Logging: crate::logging::Logging {}
#[cfg(feature = "logging")]
impl<T> Logging for T where T: crate::logging::Logging {}
#[cfg(not(feature = "logging"))]
pub trait Logging {}
#[cfg(not(feature = "logging"))]
impl<T> Logging for T {}
}
pub trait Transport<Incoming, Outgoing, Error>:
Sink<Outgoing, Error = crate::Error<Error>>
+ Stream<Item = Result<Incoming, Error>>
+ conditional::Logging
{
}
impl<T, Incoming, Outgoing, Error> Transport<Incoming, Outgoing, Error> for T where
T: Sink<Outgoing, Error = crate::Error<Error>>
+ Stream<Item = Result<Incoming, Error>>
+ conditional::Logging
{
}
pub trait SymmetricTransport<Message, Error>: Transport<Message, Message, Error> {}
impl<T, Message, Error> SymmetricTransport<Message, Error> for T where
T: Transport<Message, Message, Error>
{
}