Skip to main content

dnet_base/
lib.rs

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