use futures::{
channel::oneshot,
future::{self, FusedFuture},
stream::{self, FusedStream},
task::{Context, Poll},
FutureExt, Stream, StreamExt,
};
use std::{marker::Unpin, pin::Pin};
pub struct ShutdownStream<S> {
shutdown: future::Fuse<oneshot::Receiver<()>>,
stream: S,
}
impl<S: Stream> ShutdownStream<stream::Fuse<S>> {
pub fn new(shutdown: oneshot::Receiver<()>, stream: S) -> Self {
Self {
shutdown: shutdown.fuse(),
stream: stream.fuse(),
}
}
}
impl<S: Stream + FusedStream> ShutdownStream<S> {
pub fn from_fused(shutdown: oneshot::Receiver<()>, stream: S) -> Self {
Self {
shutdown: shutdown.fuse(),
stream,
}
}
}
impl<S: Stream<Item = T> + FusedStream + Unpin, T> Stream for ShutdownStream<S> {
type Item = T;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
if !self.shutdown.is_terminated() {
if let Poll::Ready(_) = self.shutdown.poll_unpin(cx) {
return Poll::Ready(None);
}
if !self.stream.is_terminated() {
return self.stream.poll_next_unpin(cx);
}
}
Poll::Ready(None)
}
}
impl<S: Stream<Item = T> + FusedStream + Unpin, T> FusedStream for ShutdownStream<S> {
fn is_terminated(&self) -> bool {
self.shutdown.is_terminated() || self.stream.is_terminated()
}
}