use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use pin_project::{pin_project, pinned_drop};
use crate::{imp::FutureLocalKey, FutureLocalStorage};
impl<F: Future> FutureLocalStorage for F {
fn with_scope<T, S>(self, scope: &'static S, value: T) -> ScopedFutureWithValue<T, Self>
where
T: Send,
S: AsRef<FutureLocalKey<T>>,
{
let scope = scope.as_ref();
ScopedFutureWithValue {
inner: self,
scope,
value: Some(value),
}
}
}
#[pin_project]
#[derive(Debug)]
pub struct ScopedFuture<T, F>(#[pin] ScopedFutureWithValue<T, F>)
where
T: Send + 'static,
F: Future;
impl<T, F> Future for ScopedFuture<T, F>
where
T: Send,
F: Future,
{
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().0.poll(cx).map(|(_value, result)| result)
}
}
impl<T, F> ScopedFutureWithValue<T, F>
where
T: Send,
F: Future,
{
pub fn discard_value(self) -> ScopedFuture<T, F> {
ScopedFuture(self)
}
}
#[pin_project(PinnedDrop)]
#[derive(Debug)]
pub struct ScopedFutureWithValue<T, F>
where
T: Send + 'static,
F: Future,
{
#[pin]
inner: F,
scope: &'static FutureLocalKey<T>,
value: Option<T>,
}
#[pinned_drop]
impl<T, F> PinnedDrop for ScopedFutureWithValue<T, F>
where
F: Future,
T: Send + 'static,
{
fn drop(self: Pin<&mut Self>) {}
}
impl<T, F> Future for ScopedFutureWithValue<T, F>
where
T: Send,
F: Future,
{
type Output = (T, F::Output);
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
FutureLocalKey::swap(this.scope, this.value);
let result = this.inner.poll(cx);
FutureLocalKey::swap(this.scope, this.value);
let result = std::task::ready!(result);
let value = this.value.take().unwrap();
Poll::Ready((value, result))
}
}
impl<T, F> From<ScopedFutureWithValue<T, F>> for ScopedFuture<T, F>
where
T: Send,
F: Future,
{
fn from(value: ScopedFutureWithValue<T, F>) -> Self {
Self(value)
}
}