#![no_std]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
use core::{
convert::Infallible,
future::Future,
pin::Pin,
task::ready,
task::{Context, Poll},
};
use futures_core::future::{FusedFuture, TryFuture};
use pin_project::pin_project;
#[pin_project]
#[derive(Debug, Clone, Default)]
pub struct TryForegroundBackground<F, B> {
#[pin]
foreground: F,
#[pin]
background: B,
}
#[must_use]
#[inline(always)]
fn assert_future<F: Future>(fut: F) -> F {
fut
}
#[must_use]
#[inline(always)]
fn unwrap_infallible<T>(res: Result<T, Infallible>) -> T {
res.expect("can never be Infallible")
}
impl<F, B> Future for TryForegroundBackground<F, B>
where
F: Future,
B: TryFuture<Ok = ()> + FusedFuture,
{
type Output = Result<F::Output, B::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
match this.foreground.poll(cx) {
Poll::Ready(value) => Poll::Ready(Ok(value)),
Poll::Pending if this.background.is_terminated() => Poll::Pending,
Poll::Pending => match ready!(this.background.try_poll(cx)) {
Ok(()) => Poll::Pending,
Err(err) => Poll::Ready(Err(err)),
},
}
}
}
#[pin_project]
#[derive(Debug, Clone, Default)]
struct NeverError<F> {
#[pin]
fut: F,
}
impl<F: Future> Future for NeverError<F> {
type Output = Result<F::Output, Infallible>;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().fut.poll(cx).map(Ok)
}
}
impl<F: FusedFuture> FusedFuture for NeverError<F> {
fn is_terminated(&self) -> bool {
self.fut.is_terminated()
}
}
#[pin_project]
#[derive(Debug, Clone, Default)]
pub struct ForegroundBackground<F, B> {
#[pin]
inner: TryForegroundBackground<F, NeverError<B>>,
}
impl<F, B> Future for ForegroundBackground<F, B>
where
F: Future,
B: Future<Output = ()> + FusedFuture,
{
type Output = F::Output;
#[inline(always)]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().inner.poll(cx).map(unwrap_infallible)
}
}
impl<F, B> FusedFuture for ForegroundBackground<F, B>
where
F: FusedFuture,
B: Future<Output = ()> + FusedFuture,
{
fn is_terminated(&self) -> bool {
self.inner.foreground.is_terminated()
}
}
pub trait FutureExt: Sized + Future {
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[inline]
fn with_background<B>(self, background: B) -> ForegroundBackground<Self, B>
where
B: Future<Output = ()> + FusedFuture,
{
assert_future(ForegroundBackground {
inner: self.with_try_background(NeverError { fut: background }),
})
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[inline]
fn with_try_background<B>(self, background: B) -> TryForegroundBackground<Self, B>
where
B: TryFuture<Ok = ()> + FusedFuture,
{
assert_future(TryForegroundBackground {
foreground: self,
background,
})
}
}
impl<F: Future> FutureExt for F {}