use crate::{Transformer, error::SocketError, protocol::StreamParser};
use futures::Stream;
use pin_project::pin_project;
use std::{
collections::VecDeque,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
#[derive(Debug)]
#[pin_project]
pub struct ExchangeStream<Protocol, InnerStream, StreamTransformer>
where
Protocol: StreamParser<StreamTransformer::Input>,
InnerStream: Stream,
StreamTransformer: Transformer,
{
#[pin]
pub stream: InnerStream,
pub transformer: StreamTransformer,
pub buffer: VecDeque<Result<StreamTransformer::Output, StreamTransformer::Error>>,
pub protocol_marker: PhantomData<Protocol>,
}
impl<Protocol, InnerStream, StreamTransformer> Stream
for ExchangeStream<Protocol, InnerStream, StreamTransformer>
where
Protocol: StreamParser<StreamTransformer::Input>,
InnerStream: Stream<Item = Result<Protocol::Message, Protocol::Error>> + Unpin,
StreamTransformer: Transformer,
StreamTransformer::Error: From<SocketError>,
{
type Item = Result<StreamTransformer::Output, StreamTransformer::Error>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
loop {
if let Some(output) = self.buffer.pop_front() {
return Poll::Ready(Some(output));
}
let input = match self.as_mut().project().stream.poll_next(cx) {
Poll::Ready(Some(input)) => input,
Poll::Ready(None) => return Poll::Ready(None),
Poll::Pending => return Poll::Pending,
};
let exchange_message = match Protocol::parse(input) {
Some(Ok(exchange_message)) => exchange_message,
Some(Err(err)) => return Poll::Ready(Some(Err(err.into()))),
None => continue,
};
self.transformer
.transform(exchange_message)
.into_iter()
.for_each(
|output_result: Result<StreamTransformer::Output, StreamTransformer::Error>| {
self.buffer.push_back(output_result)
},
);
}
}
}
impl<Protocol, InnerStream, StreamTransformer>
ExchangeStream<Protocol, InnerStream, StreamTransformer>
where
Protocol: StreamParser<StreamTransformer::Input>,
InnerStream: Stream,
StreamTransformer: Transformer,
{
pub fn new(
stream: InnerStream,
transformer: StreamTransformer,
buffer: VecDeque<Result<StreamTransformer::Output, StreamTransformer::Error>>,
) -> Self {
Self {
stream,
transformer,
buffer,
protocol_marker: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures::StreamExt;
struct SkipZeroParser;
impl StreamParser<i32> for SkipZeroParser {
type Stream = futures::stream::Empty<i32>;
type Message = i32;
type Error = SocketError;
fn parse(input: Result<i32, SocketError>) -> Option<Result<i32, SocketError>> {
match input {
Ok(0) => None,
Ok(n) if n < 0 => Some(Err(SocketError::Sink)),
other => Some(other),
}
}
}
struct TwoOutputs;
impl Transformer for TwoOutputs {
type Error = SocketError;
type Input = i32;
type Output = i32;
type OutputIter = Vec<Result<i32, SocketError>>;
fn transform(&mut self, input: i32) -> Self::OutputIter {
vec![Ok(input), Ok(input * 10)]
}
}
#[tokio::test]
async fn exchange_stream_parses_skips_and_buffers_until_the_inner_stream_ends() {
let inner = futures::stream::iter([Ok(1), Ok(0), Ok(-1), Ok(2)]);
let stream =
ExchangeStream::<SkipZeroParser, _, _>::new(inner, TwoOutputs, VecDeque::new());
let outputs = stream
.map(|result| result.map_err(|error| error.to_string()))
.collect::<Vec<_>>()
.await;
assert_eq!(
outputs,
vec![
Ok(1),
Ok(10),
Err(SocketError::Sink.to_string()),
Ok(2),
Ok(20),
]
);
}
}