mod type_stack;
use std::{
cell::RefCell,
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use self::type_stack::TypeStack;
struct DropGuard<I: 'static>(PhantomData<I>);
impl<I: 'static> Drop for DropGuard<I> {
fn drop(&mut self) {
YIELD_STACK.with(|s| s.borrow_mut().compact::<YieldSlot<I>>());
}
}
pin_project_lite::pin_project! {
pub struct FromFn<F, I: 'static> {
guard: DropGuard<I>,
#[pin]
future: F,
closed: bool,
}
}
pin_project_lite::pin_project! {
pub struct TryYield<F, T, E> {
yielded: PhantomData<Result<T, E>>,
#[pin]
future: F,
closed: bool,
}
}
pub struct Yielder<I>(PhantomData<I>);
#[must_use]
pub struct YieldFuture<I>(Option<I>);
impl<I> Unpin for YieldFuture<I> {}
impl<I: 'static> Yielder<I> {
pub fn yield_(&self, item: I) -> YieldFuture<I> {
YieldFuture(Some(item))
}
}
impl<T: 'static, E: 'static> Yielder<Result<T, E>> {
pub fn yield_ok(&self, item: T) -> YieldFuture<Result<T, E>> {
self.yield_(Ok(item))
}
pub fn yield_err(&self, error: E) -> YieldFuture<Result<T, E>> {
self.yield_(Err(error))
}
}
pub fn from_fn<F, Fut, I: 'static>(func: F) -> FromFn<Fut, I>
where
F: FnOnce(Yielder<I>) -> Fut,
Fut: Future<Output = ()>,
{
FromFn {
guard: DropGuard(PhantomData),
future: func(Yielder(PhantomData)),
closed: false,
}
}
pub fn try_from_fn<F, Fut, T, E>(func: F) -> FromFn<TryYield<Fut, T, E>, Result<T, E>>
where
F: FnOnce(Yielder<Result<T, E>>) -> Fut,
Fut: Future<Output = Result<(), E>>,
{
FromFn {
guard: DropGuard(PhantomData),
future: TryYield {
yielded: PhantomData,
future: func(Yielder(PhantomData)),
closed: false,
},
closed: false,
}
}
thread_local! {
static YIELD_STACK: RefCell<TypeStack> = const { RefCell::new(TypeStack::new()) };
}
enum YieldSlot<T> {
Filled(T),
Empty,
}
impl<T> YieldSlot<T> {
fn into_option(self) -> Option<T> {
match self {
Self::Filled(item) => Some(item),
Self::Empty => None,
}
}
fn into_poll(self) -> Poll<T> {
match self {
Self::Filled(item) => Poll::Ready(item),
Self::Empty => Poll::Pending,
}
}
}
struct YieldGuard<T: 'static>(bool, PhantomData<T>);
impl<T: 'static> YieldGuard<T> {
fn guard() -> Self {
YIELD_STACK.with(|c| c.borrow_mut().push::<YieldSlot<T>>(YieldSlot::Empty));
Self(true, PhantomData)
}
fn disarm(mut self) -> YieldSlot<T> {
self.0 = false;
YIELD_STACK.with(|c| c.borrow_mut().pop::<YieldSlot<T>>().expect("slot exists"))
}
}
impl<T: 'static> Drop for YieldGuard<T> {
fn drop(&mut self) {
if self.0 {
YIELD_STACK.with(|c| c.borrow_mut().pop::<YieldSlot<T>>());
}
}
}
impl<F, I> futures_core::Stream for FromFn<F, I>
where
F: Future<Output = ()>,
I: 'static,
{
type Item = I;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.project();
if *this.closed {
return Poll::Ready(None);
}
let guard = YieldGuard::<I>::guard();
*this.closed = this.future.poll(cx).is_ready();
let item = guard.disarm();
if *this.closed {
Poll::Ready(item.into_option())
} else {
item.into_poll().map(Some)
}
}
}
impl<I> Future for YieldFuture<I>
where
I: 'static,
{
type Output = ();
fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
if let Some(item) = self.0.take() {
YIELD_STACK.with(|c| {
let mut guard = c.borrow_mut();
let entry = guard.get_mut::<YieldSlot<I>>().expect("Slot exists");
*entry = YieldSlot::Filled(item);
});
return Poll::Pending;
}
Poll::Ready(())
}
}
impl<F, T, E> Future for TryYield<F, T, E>
where
F: Future<Output = Result<(), E>>,
Result<T, E>: 'static,
{
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.closed {
return Poll::Ready(());
}
let this = self.project();
let res = std::task::ready!(this.future.poll(cx));
*this.closed = true;
if let Err(e) = res {
YIELD_STACK.with(|c| {
let mut guard = c.borrow_mut();
let entry = guard
.get_mut::<YieldSlot<Result<T, E>>>()
.expect("Slot exists");
*entry = YieldSlot::Filled(Err(e));
});
return Poll::Pending;
}
Poll::Ready(())
}
}