use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use sugars_collections::ZeroOneOrMany;
use tokio::sync::oneshot;
pub auto trait NotResult {}
impl<T, E> !NotResult for Result<T, E> {}
pub struct AsyncTask<T>
where
T: NotResult, {
pub(super) receiver: oneshot::Receiver<T>,
}
impl<T> AsyncTask<T>
where
T: NotResult, {
pub fn new(receivers: ZeroOneOrMany<oneshot::Receiver<T>>) -> Self
where
T: Send + 'static,
{
match receivers {
ZeroOneOrMany::None => {
let (tx, rx) = oneshot::channel();
drop(tx); Self { receiver: rx }
}
ZeroOneOrMany::One(receiver) => Self { receiver },
ZeroOneOrMany::Many(receivers) => {
if let Some(receiver) = receivers.into_iter().next() {
Self { receiver }
} else {
let (tx, rx) = oneshot::channel();
drop(tx);
Self { receiver: rx }
}
}
}
}
pub fn from_future<F>(future: F) -> Self
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static,
{
let (tx, rx) = oneshot::channel();
tokio::spawn(async move {
let result = future.await;
let _ = tx.send(result);
});
Self { receiver: rx }
}
pub fn from_value(value: T) -> Self
where
T: Send + 'static,
{
let (tx, rx) = oneshot::channel();
let _ = tx.send(value);
Self { receiver: rx }
}
pub fn spawn<F>(f: F) -> Self
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
let (tx, rx) = oneshot::channel();
tokio::task::spawn_blocking(move || {
let result = f();
let _ = tx.send(result);
});
Self { receiver: rx }
}
}
impl<T> Future for AsyncTask<T>
where
T: NotResult, {
type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match Pin::new(&mut self.receiver).poll(cx) {
Poll::Ready(Ok(result)) => Poll::Ready(result),
Poll::Ready(Err(_)) => panic!("AsyncTask channel closed unexpectedly"),
Poll::Pending => Poll::Pending,
}
}
}