use std::pin::Pin;
use std::task::{Context, Poll};
use futures_core::Stream;
use pin_project::pin_project;
use crate::processors::Processor;
use crate::processors::buffered::{Buffer, BufferReceiver, BufferSender};
pub trait ProcessorExt<T>: Processor<T>
where
Self: 'static,
T: 'static,
{
fn into_stream<S: Stream<Item = T>>(self, input_stream: S) -> ProcessorStream<Self, S, T>
where
Self: Sized,
{
ProcessorStream::new(self, input_stream)
}
}
impl<P, T: 'static> ProcessorExt<T> for P where P: Processor<T> + 'static {}
#[pin_project]
#[must_use = "streams do nothing unless polled"]
pub struct ProcessorStream<P, S, T>
where
P: Processor<T>,
S: Stream<Item = T>,
{
#[pin]
input_stream: S,
buffer: Buffer,
tx: BufferSender<T>,
rx: BufferReceiver<P, T>,
}
impl<P, S, T> ProcessorStream<P, S, T>
where
P: Processor<T> + 'static,
S: Stream<Item = T>,
T: 'static,
{
pub(crate) fn new(processor: P, input_stream: S) -> ProcessorStream<P, S, T> {
let (buffer, tx, rx) = Buffer::new(processor);
ProcessorStream {
input_stream,
buffer,
tx,
rx,
}
}
}
impl<P, S, T> Stream for ProcessorStream<P, S, T>
where
P: Processor<T>,
S: Stream<Item = T>,
{
type Item = Result<P::Output, P::Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.project();
let mut made_progress = false;
match this.input_stream.as_mut().poll_next(cx) {
Poll::Ready(Some(input)) => {
let _ = this.tx.send(input);
made_progress = true;
}
Poll::Ready(None) | Poll::Pending => (),
};
match this.rx.poll_recv(cx) {
Poll::Ready(item) => Poll::Ready(item),
Poll::Pending => {
if made_progress {
cx.waker().wake_by_ref();
}
Poll::Pending
}
}
}
}
pub trait StreamLayerExt<T>: Stream<Item = T> + Sized
where
T: 'static,
{
fn layer<P>(self, processor: P) -> ProcessorStream<P, Self, T>
where
P: Processor<T> + 'static,
{
ProcessorStream::new(processor, self)
}
}
impl<S, T: 'static> StreamLayerExt<T> for S where S: Stream<Item = T> {}