use std::{
pin::Pin,
task::{Context, Poll},
};
use std::sync::atomic::{AtomicBool, Ordering};
use futures::Stream;
pub struct StreamWithFinalizationCallbacks<S, FComplete, FCancel>
where
S: Stream,
FComplete: FnOnce(),
FCancel: FnOnce(),
{
inner: S,
complete_fn: Option<FComplete>,
cancel_fn: Option<FCancel>,
finalized: AtomicBool,
}
impl<S, FComplete, FCancel> StreamWithFinalizationCallbacks<S, FComplete, FCancel>
where
S: Stream,
FComplete: FnOnce(),
FCancel: FnOnce(),
{
pub fn new(inner: S, complete_cb: FComplete, cancel_cb: FCancel) -> Self {
StreamWithFinalizationCallbacks {
inner,
complete_fn: Some(complete_cb),
cancel_fn: Some(cancel_cb),
finalized: AtomicBool::new(false),
}
}
}
impl<S, FComplete, FCancel, T> Stream for StreamWithFinalizationCallbacks<S, FComplete, FCancel>
where
S: Stream<Item = T> + Unpin,
FComplete: FnOnce(),
FCancel: FnOnce(),
{
type Item = T;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this: &mut Self = unsafe { self.get_unchecked_mut() };
match Pin::new(&mut this.inner).poll_next(cx) {
Poll::Ready(Some(item)) => Poll::Ready(Some(item)),
Poll::Ready(None) => {
if let Some(complete_fn) = this.complete_fn.take() {
let finalized = this.finalized.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| Some(true)).unwrap_or_default();
if !finalized {
complete_fn();
}
}
Poll::Ready(None)
}
Poll::Pending => Poll::Pending,
}
}
}
impl<S, FComplete, FCancel> Drop for StreamWithFinalizationCallbacks<S, FComplete, FCancel>
where
S: Stream,
FComplete: FnOnce(),
FCancel: FnOnce(),
{
fn drop(&mut self) {
if let Some(cancel_fn) = self.cancel_fn.take() {
let finalized = self.finalized.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| Some(true)).unwrap_or_default();
if !finalized {
cancel_fn();
}
}
}
}
fn no_op() {}
pub trait StreamExtFinalizationCallbacks: Stream + Sized {
fn on_complete<FComplete>(
self,
complete_cb: FComplete,
) -> StreamWithFinalizationCallbacks<Self, FComplete, impl FnOnce()>
where
FComplete: FnOnce(),
{
StreamWithFinalizationCallbacks::new(self, complete_cb, no_op)
}
fn on_cancellation<FCancel>(
self,
cancel_cb: FCancel,
) -> StreamWithFinalizationCallbacks<Self, impl FnOnce(), FCancel>
where
FCancel: FnOnce(),
{
StreamWithFinalizationCallbacks::new(self, no_op, cancel_cb)
}
}
impl<S: Stream> StreamExtFinalizationCallbacks for S {}