use futures::Stream;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::sync::{mpsc, oneshot};
type BoxedVecFuture<T, E> = Pin<Box<dyn Future<Output = Result<Vec<T>, E>> + Send>>;
type FutureFactory<T, E> = Box<dyn FnOnce() -> BoxedVecFuture<T, E> + Send>;
pub struct AsyncTask<T> {
rx: oneshot::Receiver<T>,
}
impl<T> AsyncTask<T>
where
T: Send + 'static,
{
#[inline]
#[must_use]
pub fn new(rx: oneshot::Receiver<T>) -> Self {
Self { rx }
}
#[inline]
pub fn spawn<F>(f: F) -> Self
where
F: FnOnce() -> T + Send + 'static,
{
let (tx, rx) = oneshot::channel();
tokio::task::spawn_blocking(move || {
let _ = tx.send(f());
});
Self::new(rx)
}
#[inline]
pub fn spawn_async<F>(future: F) -> Self
where
F: Future<Output = T> + Send + 'static,
{
let (tx, rx) = oneshot::channel();
tokio::task::spawn(async move {
let _ = tx.send(future.await);
});
Self::new(rx)
}
}
impl<T> Future for AsyncTask<T> {
type Output = Result<T, oneshot::error::RecvError>;
#[inline]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.rx).poll(cx)
}
}
pub struct AsyncStream<T> {
rx: mpsc::UnboundedReceiver<T>,
}
impl<T> AsyncStream<T> {
#[inline]
#[must_use]
pub fn new(rx: mpsc::UnboundedReceiver<T>) -> Self {
Self { rx }
}
#[must_use]
pub fn from_vec(items: Vec<T>) -> Self
where
T: Send + 'static,
{
let (tx, rx) = mpsc::unbounded_channel();
tokio::task::spawn(async move {
for item in items {
if tx.send(item).is_err() {
break; }
}
});
Self::new(rx)
}
}
impl<T> Stream for AsyncStream<T> {
type Item = T;
#[inline]
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.rx.poll_recv(cx)
}
}
pub struct EmitterBuilder<T, E> {
future_factory: FutureFactory<T, E>,
}
impl<T, E> EmitterBuilder<T, E>
where
T: Send + 'static,
E: Send + 'static,
{
#[must_use]
pub fn new(future_factory: FutureFactory<T, E>) -> Self {
Self { future_factory }
}
pub fn emit<F, G>(self, transform: F, on_error: G) -> AsyncStream<Result<T, E>>
where
F: Fn(T) -> T + Send + 'static,
G: Fn(&E) + Send + 'static,
{
let (tx, rx) = mpsc::unbounded_channel();
tokio::spawn(async move {
let future = (self.future_factory)();
match future.await {
Ok(items) => {
for item in items {
let transformed = transform(item);
if tx.send(Ok(transformed)).is_err() {
break; }
}
}
Err(e) => {
on_error(&e);
let _ = tx.send(Err(e));
}
}
});
AsyncStream::new(rx)
}
}