Skip to main content

dnet_utils/
latest.rs

1//! Wrapper transport turning a [numbered] transport into a transport discarding
2//! old messages.
3//!
4//! Polling a transport for the next message will return the latest
5//! received message, ignoring messages received before.
6//!
7//! Potentially useful when user doesn't care about stale messages
8//! (for example: multiplayer video games).
9//!
10//! [numbered]: super::number::Number
11
12use std::{
13    marker::PhantomData,
14    pin::Pin,
15    task::{Context, Poll},
16};
17
18use futures::{stream::FusedStream, Sink, Stream, StreamExt};
19use pin_project::pin_project;
20
21use super::number::Number;
22
23/// Trait for requesting only the most recent messages.
24pub trait OnlyLatest<N, Incoming, Outgoing, Error>:
25    dnet_base::Transport<Incoming, Outgoing, Error> + Sized + Unpin
26where
27    Error: std::error::Error,
28{
29    /// Wrap transport with [Latest] transport turning it into transport returning latest
30    /// message when polling it for the next received message.
31    fn only_latest(self) -> Latest<Self, Error, N, Incoming, Outgoing>
32    where
33        Incoming: Number<Output = N>,
34        for<'a> &'a N: PartialOrd,
35    {
36        Latest::new(self)
37    }
38}
39
40impl<T, N, Incoming, Outgoing, Error> OnlyLatest<N, Incoming, Outgoing, Error> for T
41where
42    T: dnet_base::Transport<Incoming, Outgoing, Error> + Unpin,
43    Error: std::error::Error,
44{
45}
46
47/// Wrapper transport turning a [numbered] transport into a transport discarding
48/// old messages (polling a transport for the next message will return the latest
49/// received message, ignoring messages received before).
50///
51/// [numbered]: super::number::Number
52#[pin_project]
53pub struct Latest<T, E, N, Incoming, Outgoing>
54where
55    T: dnet_base::Transport<Incoming, Outgoing, E>,
56    Incoming: Number<Output = N>,
57    for<'a> &'a N: PartialOrd,
58    E: std::error::Error,
59{
60    #[pin]
61    inner: T,
62    last_number: Option<N>,
63
64    #[cfg(feature = "logging")]
65    logger: dnet_base::Logger,
66
67    _error: PhantomData<E>,
68    _incoming: PhantomData<Incoming>,
69    _outgoing: PhantomData<Outgoing>,
70}
71
72impl<T, E, N, Incoming, Outgoing> Latest<T, E, N, Incoming, Outgoing>
73where
74    T: dnet_base::Transport<Incoming, Outgoing, E>,
75    Incoming: Number<Output = N>,
76    for<'a> &'a N: PartialOrd,
77    E: std::error::Error,
78{
79    /// Wrap a provided [numbered] transport turning it into ordered transport returning latest
80    /// message when polling it for the next received message.
81    ///
82    /// [numbered]: super::number::Number
83    pub fn new(transport: T) -> Self {
84        Latest {
85            inner: transport,
86            last_number: None,
87
88            #[cfg(feature = "logging")]
89            logger: dnet_base::Logger::new::<Self>(),
90
91            _error: PhantomData,
92            _incoming: PhantomData,
93            _outgoing: PhantomData,
94        }
95    }
96}
97
98impl<T, E, N, Incoming, Outgoing> Sink<Outgoing> for Latest<T, E, N, Incoming, Outgoing>
99where
100    T: dnet_base::Transport<Incoming, Outgoing, E>,
101    Incoming: Number<Output = N>,
102    for<'a> &'a N: PartialOrd,
103    E: std::error::Error,
104{
105    type Error = dnet_base::Error<E>;
106
107    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
108        let me = self.project();
109        let result = me.inner.poll_ready(cx);
110
111        #[cfg(feature = "logging")]
112        me.logger.log_ready(&result);
113
114        result
115    }
116
117    fn start_send(self: Pin<&mut Self>, item: Outgoing) -> Result<(), Self::Error> {
118        let me = self.project();
119        let result = me.inner.start_send(item);
120
121        #[cfg(feature = "logging")]
122        match &result {
123            Ok(_) => me.logger.log_message_preparation_success::<Outgoing>(None),
124            Err(error) => me.logger.log_sending_failure(error),
125        }
126
127        result
128    }
129
130    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
131        let me = self.project();
132        let result = me.inner.poll_flush(cx);
133
134        #[cfg(feature = "logging")]
135        me.logger.log_flush(&result);
136
137        result
138    }
139
140    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
141        let me = self.project();
142        let result = me.inner.poll_close(cx);
143
144        #[cfg(feature = "logging")]
145        me.logger.log_close(&result);
146
147        result
148    }
149}
150
151impl<T, E, N, Incoming, Outgoing> Stream for Latest<T, E, N, Incoming, Outgoing>
152where
153    T: dnet_base::Transport<Incoming, Outgoing, E>,
154    Incoming: Number<Output = N>,
155    for<'a> &'a N: PartialOrd,
156    E: std::error::Error,
157{
158    type Item = Result<Incoming, E>;
159
160    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
161        let mut me = self.project();
162        let mut latest = None;
163        let result = loop {
164            match me.inner.poll_next_unpin(cx) {
165                Poll::Ready(item) => {
166                    if let Some(item) = item {
167                        let item = item?;
168                        let number = item.number();
169
170                        if let Some(last_number) = me.last_number.as_ref() {
171                            if &number > last_number {
172                                latest = Some(item);
173                                *me.last_number = Some(number);
174                            }
175
176                            #[cfg(feature = "logging")]
177                            me.logger.log_incoming_filtered_out::<Incoming>();
178                        } else {
179                            latest = Some(item);
180                            *me.last_number = Some(number);
181                        }
182                    } else {
183                        break Poll::Ready(None);
184                    }
185                }
186                Poll::Pending => {
187                    break if let Some(latest) = latest {
188                        Poll::Ready(Some(Ok(latest)))
189                    } else {
190                        Poll::Pending
191                    }
192                }
193            }
194        };
195
196        #[cfg(feature = "logging")]
197        me.logger.log_receiving(&result, None);
198
199        result
200    }
201}
202
203impl<T, E, N, Incoming, Outgoing> FusedStream for Latest<T, E, N, Incoming, Outgoing>
204where
205    T: dnet_base::Transport<Incoming, Outgoing, E> + FusedStream,
206    Incoming: Number<Output = N>,
207    for<'a> &'a N: PartialOrd,
208    E: std::error::Error,
209{
210    fn is_terminated(&self) -> bool {
211        self.inner.is_terminated()
212    }
213}
214
215#[cfg(feature = "logging")]
216impl<T, E, N, Incoming, Outgoing> dnet_base::Logging for Latest<T, E, N, Incoming, Outgoing>
217where
218    T: dnet_base::Transport<Incoming, Outgoing, E> + dnet_base::Logging,
219    Incoming: Number<Output = N>,
220    for<'a> &'a N: PartialOrd,
221    E: std::error::Error,
222{
223    const KIND: &'static str = "Latest";
224
225    fn with_logger<F, R>(&self, f: F) -> R
226    where
227        F: FnOnce(&dnet_base::Logger) -> R,
228    {
229        f(&self.logger)
230    }
231
232    fn with_logger_mut<F, R>(&mut self, f: F) -> R
233    where
234        F: FnOnce(&mut dnet_base::Logger) -> R,
235    {
236        f(&mut self.logger)
237    }
238}
239
240#[cfg(test)]
241mod tests {
242    use dnet_base::Receive;
243    use dnet_tests::{dtest, dtest_configure};
244    use futures::SinkExt;
245
246    use crate::{
247        channel::transports,
248        latest::OnlyLatest,
249        number::{NumberMessagesU128, NumberMessagesU32, Wrapper},
250        unwrap::{Unwrap, Unwrapping},
251    };
252
253    dtest_configure!();
254
255    #[dtest]
256    async fn test_transport() {
257        let (left, right) = transports();
258
259        let mut left = left.number_messages_u32().only_latest().unwrapping();
260        let mut right = right.number_messages_u32().only_latest().unwrapping();
261
262        dnet_tests::init_logging(&mut left, &mut right);
263
264        left.send(1).await.unwrap();
265        left.send(2).await.unwrap();
266        left.send(3).await.unwrap();
267
268        assert_eq!(right.receive().await.unwrap(), 3);
269
270        right.send(1).await.unwrap();
271        right.send(2).await.unwrap();
272        right.send(3).await.unwrap();
273
274        assert_eq!(left.receive().await.unwrap(), 3);
275    }
276
277    #[dtest]
278    async fn test_order() {
279        let (left, right) = transports();
280
281        let mut left = left.only_latest();
282        let mut right = right.number_messages_u128().only_latest();
283
284        dnet_tests::init_logging(&mut left, &mut right);
285
286        left.send(Wrapper {
287            number: 1,
288            wrapped: 2,
289        })
290        .await
291        .unwrap();
292        left.send(Wrapper {
293            number: 2,
294            wrapped: 3,
295        })
296        .await
297        .unwrap();
298        left.send(Wrapper {
299            number: 0,
300            wrapped: 1,
301        })
302        .await
303        .unwrap();
304
305        right.send(1).await.unwrap();
306
307        assert_eq!(right.receive().await.unwrap().unwrap(), 3);
308        assert_eq!(left.receive().await.unwrap().unwrap(), 1);
309    }
310}