1#![warn(missing_docs)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4#[cfg(feature = "logging")]
7pub mod logging;
8#[cfg(feature = "logging")]
9pub use logging::{Logger, Logging};
10
11use std::{
12 fmt::Display,
13 io::{Read, Write},
14 pin::Pin,
15 task::{Context, Poll},
16};
17
18use futures::{
19 future::FusedFuture,
20 ready,
21 stream::{FusedStream, Next},
22 Future, FutureExt, Sink, Stream, StreamExt,
23};
24use pin_project::pin_project;
25use serde::{Deserialize, Serialize};
26
27pub trait Encode {
29 type Error: std::error::Error;
31
32 fn encode<W, T>(&mut self, writer: W, message: &T) -> Result<(), Self::Error>
34 where
35 W: Write,
36 T: Serialize;
37}
38
39pub trait Decode {
41 type Error: std::error::Error;
43
44 fn decode<R, T>(&mut self, data: R) -> Result<T, Self::Error>
46 where
47 R: Read,
48 for<'de> T: Deserialize<'de>;
49}
50
51pub trait Codec: Encode + Decode {}
53
54impl<T> Codec for T where T: Encode + Decode {}
55
56#[derive(Debug, PartialEq, Eq)]
58pub enum Error<Other> {
59 Closed,
61
62 Other(Other),
64}
65
66impl<Other> Error<Other> {
67 pub fn closed(&self) -> bool {
69 matches!(self, Error::Closed)
70 }
71}
72
73impl<Other> Display for Error<Other>
74where
75 Other: Display,
76{
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 match self {
79 Self::Closed => write!(f, "transport closed"),
80 Self::Other(other) => write!(f, "{other}"),
81 }
82 }
83}
84
85impl<Other> std::error::Error for Error<Other> where Other: std::error::Error {}
86
87pub trait Receive<Message, Error> {
89 fn receive(&mut self) -> Recv<'_, Self>;
91}
92
93impl<T, Message, Error> Receive<Message, Error> for T
94where
95 T: Stream<Item = Result<Message, Error>> + Unpin,
96{
97 fn receive(&mut self) -> Recv<'_, Self> {
99 let next = self.next();
100 Recv {
101 next,
102 terminated: false,
103 }
104 }
105}
106
107pub struct Recv<'a, T>
111where
112 T: ?Sized,
113{
114 next: Next<'a, T>,
115 terminated: bool,
116}
117
118impl<T, Message, Error> Future for Recv<'_, T>
119where
120 T: Stream<Item = Result<Message, Error>> + Unpin,
121{
122 type Output = Result<Message, self::Error<Error>>;
123
124 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
125 match self.next.poll_unpin(cx) {
126 Poll::Ready(item) => {
127 self.terminated = true;
128 if let Some(item) = item {
129 Poll::Ready(item.map_err(|error| self::Error::Other(error)))
130 } else {
131 Poll::Ready(Err(self::Error::Closed))
132 }
133 }
134 Poll::Pending => Poll::Pending,
135 }
136 }
137}
138
139impl<T, Message, Error> FusedFuture for Recv<'_, T>
140where
141 T: Stream<Item = Result<Message, Error>> + Unpin,
142{
143 fn is_terminated(&self) -> bool {
144 self.terminated
145 }
146}
147
148pub trait Messages<T, Message, Error>
150where
151 Self: Sized,
152{
153 fn messages_with_error_callback<F>(self, error_callback: F) -> MessageStream<Self, F>
157 where
158 F: FnMut(Error);
159
160 fn messages(self) -> MessageStream<Self, fn(Error) -> ()> {
162 self.messages_with_error_callback(|_| {})
163 }
164}
165
166impl<T, Message, Error> Messages<T, Message, Error> for T
167where
168 T: Stream<Item = Result<Message, Error>> + Unpin,
169{
170 fn messages_with_error_callback<F>(self, error_callback: F) -> MessageStream<Self, F>
171 where
172 F: FnMut(Error),
173 {
174 MessageStream {
175 stream: self,
176 error_callback,
177 terminated: false,
178 }
179 }
180}
181
182#[pin_project]
188pub struct MessageStream<T, F> {
189 #[pin]
190 stream: T,
191 error_callback: F,
192 terminated: bool,
193}
194
195impl<T, F, Message, Error> Stream for MessageStream<T, F>
196where
197 T: Stream<Item = Result<Message, Error>> + Unpin,
198 F: FnMut(Error),
199{
200 type Item = Message;
201
202 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
203 loop {
204 if let Some(result) = ready!(self.stream.poll_next_unpin(cx)) {
205 match result {
206 Ok(message) => return Poll::Ready(Some(message)),
207 Err(error) => {
208 (self.error_callback)(error);
209 continue;
210 }
211 }
212 } else {
213 self.terminated = true;
214 return Poll::Ready(None);
215 }
216 }
217 }
218}
219
220impl<T, F, Message, Error> FusedStream for MessageStream<T, F>
221where
222 T: Stream<Item = Result<Message, Error>> + Unpin,
223 F: FnMut(Error),
224{
225 fn is_terminated(&self) -> bool {
226 self.terminated
227 }
228}
229
230#[doc(hidden)]
231pub mod conditional {
233 #[cfg(feature = "logging")]
234 pub trait Logging: crate::logging::Logging {}
236
237 #[cfg(feature = "logging")]
238 impl<T> Logging for T where T: crate::logging::Logging {}
239
240 #[cfg(not(feature = "logging"))]
241 pub trait Logging {}
243
244 #[cfg(not(feature = "logging"))]
245 impl<T> Logging for T {}
246}
247
248pub trait Transport<Incoming, Outgoing, Error>:
250 Sink<Outgoing, Error = crate::Error<Error>>
251 + Stream<Item = Result<Incoming, Error>>
252 + conditional::Logging
253{
254}
255
256impl<T, Incoming, Outgoing, Error> Transport<Incoming, Outgoing, Error> for T where
257 T: Sink<Outgoing, Error = crate::Error<Error>>
258 + Stream<Item = Result<Incoming, Error>>
259 + conditional::Logging
260{
261}
262
263pub trait SymmetricTransport<Message, Error>: Transport<Message, Message, Error> {}
265
266impl<T, Message, Error> SymmetricTransport<Message, Error> for T where
267 T: Transport<Message, Message, Error>
268{
269}