use crate::future::pagination_stream::collect::sealed::Collectable;
use crate::future::rendezvous;
use pin_project_lite::pin_project;
use std::fmt;
use std::future::poll_fn;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
pin_project! {
pub struct FnStream<Item> {
#[pin]
rx: rendezvous::Receiver<Item>,
generator: Option<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
}
}
impl<Item> fmt::Debug for FnStream<Item> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let item_typename = std::any::type_name::<Item>();
write!(f, "FnStream<{item_typename}>")
}
}
impl<Item> FnStream<Item> {
pub fn new<T>(generator: T) -> Self
where
T: FnOnce(rendezvous::Sender<Item>) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
{
let (tx, rx) = rendezvous::channel::<Item>();
Self {
rx,
generator: Some(Box::pin(generator(tx))),
}
}
pub async fn next(&mut self) -> Option<Item>
where
Self: Unpin,
{
let mut me = Pin::new(self);
poll_fn(|cx| me.as_mut().poll_next(cx)).await
}
pub(crate) fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Item>> {
let mut me = self.project();
match me.rx.poll_recv(cx) {
Poll::Ready(item) => Poll::Ready(item),
Poll::Pending => {
if let Some(generator) = me.generator {
if generator.as_mut().poll(cx).is_ready() {
*me.generator = None;
}
}
Poll::Pending
}
}
}
pub async fn collect<T: Collectable<Item>>(mut self) -> T {
let mut collection = T::initialize();
while let Some(item) = self.next().await {
if !T::extend(&mut collection, item) {
break;
}
}
T::finalize(collection)
}
}
impl<T, E> FnStream<Result<T, E>> {
pub async fn try_next(&mut self) -> Result<Option<T>, E> {
self.next().await.transpose()
}
pub async fn try_collect(self) -> Result<Vec<T>, E> {
self.collect::<Result<Vec<T>, E>>().await
}
}