use std::{
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use futures::{stream::FusedStream, Sink, Stream, StreamExt};
use pin_project::pin_project;
use super::number::Number;
pub trait OnlyLatest<N, Incoming, Outgoing, Error>:
dnet_base::Transport<Incoming, Outgoing, Error> + Sized + Unpin
where
Error: std::error::Error,
{
fn only_latest(self) -> Latest<Self, Error, N, Incoming, Outgoing>
where
Incoming: Number<Output = N>,
for<'a> &'a N: PartialOrd,
{
Latest::new(self)
}
}
impl<T, N, Incoming, Outgoing, Error> OnlyLatest<N, Incoming, Outgoing, Error> for T
where
T: dnet_base::Transport<Incoming, Outgoing, Error> + Unpin,
Error: std::error::Error,
{
}
#[pin_project]
pub struct Latest<T, E, N, Incoming, Outgoing>
where
T: dnet_base::Transport<Incoming, Outgoing, E>,
Incoming: Number<Output = N>,
for<'a> &'a N: PartialOrd,
E: std::error::Error,
{
#[pin]
inner: T,
last_number: Option<N>,
#[cfg(feature = "logging")]
logger: dnet_base::Logger,
_error: PhantomData<E>,
_incoming: PhantomData<Incoming>,
_outgoing: PhantomData<Outgoing>,
}
impl<T, E, N, Incoming, Outgoing> Latest<T, E, N, Incoming, Outgoing>
where
T: dnet_base::Transport<Incoming, Outgoing, E>,
Incoming: Number<Output = N>,
for<'a> &'a N: PartialOrd,
E: std::error::Error,
{
pub fn new(transport: T) -> Self {
Latest {
inner: transport,
last_number: None,
#[cfg(feature = "logging")]
logger: dnet_base::Logger::new::<Self>(),
_error: PhantomData,
_incoming: PhantomData,
_outgoing: PhantomData,
}
}
}
impl<T, E, N, Incoming, Outgoing> Sink<Outgoing> for Latest<T, E, N, Incoming, Outgoing>
where
T: dnet_base::Transport<Incoming, Outgoing, E>,
Incoming: Number<Output = N>,
for<'a> &'a N: PartialOrd,
E: std::error::Error,
{
type Error = dnet_base::Error<E>;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let me = self.project();
let result = me.inner.poll_ready(cx);
#[cfg(feature = "logging")]
me.logger.log_ready(&result);
result
}
fn start_send(self: Pin<&mut Self>, item: Outgoing) -> Result<(), Self::Error> {
let me = self.project();
let result = me.inner.start_send(item);
#[cfg(feature = "logging")]
match &result {
Ok(_) => me.logger.log_message_preparation_success::<Outgoing>(None),
Err(error) => me.logger.log_sending_failure(error),
}
result
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let me = self.project();
let result = me.inner.poll_flush(cx);
#[cfg(feature = "logging")]
me.logger.log_flush(&result);
result
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let me = self.project();
let result = me.inner.poll_close(cx);
#[cfg(feature = "logging")]
me.logger.log_close(&result);
result
}
}
impl<T, E, N, Incoming, Outgoing> Stream for Latest<T, E, N, Incoming, Outgoing>
where
T: dnet_base::Transport<Incoming, Outgoing, E>,
Incoming: Number<Output = N>,
for<'a> &'a N: PartialOrd,
E: std::error::Error,
{
type Item = Result<Incoming, E>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut me = self.project();
let mut latest = None;
let result = loop {
match me.inner.poll_next_unpin(cx) {
Poll::Ready(item) => {
if let Some(item) = item {
let item = item?;
let number = item.number();
if let Some(last_number) = me.last_number.as_ref() {
if &number > last_number {
latest = Some(item);
*me.last_number = Some(number);
}
#[cfg(feature = "logging")]
me.logger.log_incoming_filtered_out::<Incoming>();
} else {
latest = Some(item);
*me.last_number = Some(number);
}
} else {
break Poll::Ready(None);
}
}
Poll::Pending => {
break if let Some(latest) = latest {
Poll::Ready(Some(Ok(latest)))
} else {
Poll::Pending
}
}
}
};
#[cfg(feature = "logging")]
me.logger.log_receiving(&result, None);
result
}
}
impl<T, E, N, Incoming, Outgoing> FusedStream for Latest<T, E, N, Incoming, Outgoing>
where
T: dnet_base::Transport<Incoming, Outgoing, E> + FusedStream,
Incoming: Number<Output = N>,
for<'a> &'a N: PartialOrd,
E: std::error::Error,
{
fn is_terminated(&self) -> bool {
self.inner.is_terminated()
}
}
#[cfg(feature = "logging")]
impl<T, E, N, Incoming, Outgoing> dnet_base::Logging for Latest<T, E, N, Incoming, Outgoing>
where
T: dnet_base::Transport<Incoming, Outgoing, E> + dnet_base::Logging,
Incoming: Number<Output = N>,
for<'a> &'a N: PartialOrd,
E: std::error::Error,
{
const KIND: &'static str = "Latest";
fn with_logger<F, R>(&self, f: F) -> R
where
F: FnOnce(&dnet_base::Logger) -> R,
{
f(&self.logger)
}
fn with_logger_mut<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut dnet_base::Logger) -> R,
{
f(&mut self.logger)
}
}
#[cfg(test)]
mod tests {
use dnet_base::Receive;
use dnet_tests::{dtest, dtest_configure};
use futures::SinkExt;
use crate::{
channel::transports,
latest::OnlyLatest,
number::{NumberMessagesU128, NumberMessagesU32, Wrapper},
unwrap::{Unwrap, Unwrapping},
};
dtest_configure!();
#[dtest]
async fn test_transport() {
let (left, right) = transports();
let mut left = left.number_messages_u32().only_latest().unwrapping();
let mut right = right.number_messages_u32().only_latest().unwrapping();
dnet_tests::init_logging(&mut left, &mut right);
left.send(1).await.unwrap();
left.send(2).await.unwrap();
left.send(3).await.unwrap();
assert_eq!(right.receive().await.unwrap(), 3);
right.send(1).await.unwrap();
right.send(2).await.unwrap();
right.send(3).await.unwrap();
assert_eq!(left.receive().await.unwrap(), 3);
}
#[dtest]
async fn test_order() {
let (left, right) = transports();
let mut left = left.only_latest();
let mut right = right.number_messages_u128().only_latest();
dnet_tests::init_logging(&mut left, &mut right);
left.send(Wrapper {
number: 1,
wrapped: 2,
})
.await
.unwrap();
left.send(Wrapper {
number: 2,
wrapped: 3,
})
.await
.unwrap();
left.send(Wrapper {
number: 0,
wrapped: 1,
})
.await
.unwrap();
right.send(1).await.unwrap();
assert_eq!(right.receive().await.unwrap().unwrap(), 3);
assert_eq!(left.receive().await.unwrap().unwrap(), 1);
}
}