#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
pub use futures_core;
use pin_project_lite::pin_project;
use std::{
cell::UnsafeCell,
future::Future,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub enum GeneratorState<Y, R> {
Yielded(Y),
Complete(R),
}
pub trait AsyncGenerator {
type Yield;
type Return;
fn poll_resume(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<GeneratorState<Self::Yield, Self::Return>>;
}
struct Inner<Y> {
data: UnsafeCell<Option<Y>>,
}
unsafe impl<Y: Send + Sync> Sync for Inner<Y> {}
#[doc(hidden)]
pub struct Yield<Y = ()> {
inner: Arc<Inner<Y>>,
}
#[doc(hidden)]
pub struct Return<T = ()>(T);
impl<Y> Yield<Y> {
pub async fn yield_(&mut self, val: Y) {
unsafe {
*self.inner.data.get() = Some(val);
}
std::future::poll_fn(|_| {
if unsafe { (*self.inner.data.get()).is_some() } {
return Poll::Pending;
}
Poll::Ready(())
})
.await
}
#[inline]
pub fn return_<R>(self, _v: R) -> Return<R> {
Return(_v)
}
}
pin_project! {
pub struct AsyncGen<Fut, Y> {
inner: Arc<Inner<Y>>,
#[pin]
fut: Fut,
}
}
impl<Fut, Y, R> AsyncGen<Fut, Y>
where
Fut: Future<Output = Return<R>>,
{
#[doc(hidden)]
pub fn poll_resume(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<GeneratorState<Y, R>> {
let me = self.project();
match me.fut.poll(cx) {
Poll::Ready(Return(val)) => Poll::Ready(GeneratorState::Complete(val)),
Poll::Pending => {
unsafe {
let data = &mut *me.inner.data.get();
if data.is_some() {
return Poll::Ready(GeneratorState::Yielded(data.take().unwrap()));
}
}
Poll::Pending
}
}
}
#[inline]
pub async fn resume(self: &mut Pin<&mut Self>) -> GeneratorState<Y, R> {
std::future::poll_fn(|cx| self.as_mut().poll_resume(cx)).await
}
}
impl<Fut, Y> AsyncGen<Fut, Y>
where
Fut: Future<Output = Return<()>>,
{
#[inline]
pub fn into_async_iter(self) -> AsyncIter<Self> {
AsyncIter::from(self)
}
#[doc(hidden)]
pub fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Y>> {
let me = self.project();
match me.fut.poll(cx) {
Poll::Ready(Return(())) => Poll::Ready(None),
Poll::Pending => {
unsafe {
let data = &mut *me.inner.data.get();
if data.is_some() {
return Poll::Ready(data.take());
}
}
Poll::Pending
}
}
}
}
impl<Fut, Y> futures_core::Stream for AsyncGen<Fut, Y>
where
Fut: Future<Output = Return<()>>,
{
type Item = Y;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
AsyncGen::poll_next(self, cx)
}
}
impl<Fut, Y, R> AsyncGenerator for AsyncGen<Fut, Y>
where
Fut: Future<Output = Return<R>>,
{
type Yield = Y;
type Return = R;
fn poll_resume(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<GeneratorState<Self::Yield, Self::Return>> {
AsyncGen::poll_resume(self, cx)
}
}
pin_project! {
#[derive(Clone)]
pub struct AsyncIter<G> {
#[pin]
gen: G,
}
}
impl<G> From<G> for AsyncIter<G> {
#[inline]
fn from(gen: G) -> Self {
AsyncIter { gen }
}
}
impl<G: AsyncGenerator<Return = ()>> AsyncIter<G> {
pub fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<G::Yield>> {
self.project().gen.poll_resume(cx).map(|s| match s {
GeneratorState::Yielded(val) => Some(val),
GeneratorState::Complete(()) => None,
})
}
}
impl<G: AsyncGenerator<Return = ()>> futures_core::Stream for AsyncIter<G> {
type Item = G::Yield;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
AsyncIter::poll_next(self, cx)
}
}
pub fn gen<Fut, Y, R>(fut: impl FnOnce(Yield<Y>) -> Fut) -> AsyncGen<Fut, Y>
where
Fut: Future<Output = Return<R>>,
{
let inner = Arc::new(Inner {
data: UnsafeCell::new(None),
});
let fut = fut(Yield {
inner: inner.clone(),
});
AsyncGen { inner, fut }
}
#[macro_export]
macro_rules! gen {
($($tt:tt)*) => {
$crate::__private::gen_inner!(($crate) $($tt)*)
}
}
#[doc(hidden)]
pub mod __private {
pub use async_gen_macros::*;
}