pub struct ExceptionContext<E> {
exception: core::cell::Cell<Option<E>>,
}
impl<E> Default for ExceptionContext<E> {
fn default() -> Self {
Self {
exception: core::cell::Cell::new(None),
}
}
}
impl<E> ExceptionContext<E> {
pub fn new() -> Self {
core::default::Default::default()
}
pub async fn throw(&self, exception: E) -> ! {
self.exception.set(Some(exception));
core::future::pending().await
}
pub fn catching<'a, Fu: core::future::Future, F: Fn(&'a Self) -> Fu>(
&'a self,
f: F,
) -> Catching<'a, E, Fu> {
Catching {
ctx: self,
future: f(self),
}
}
}
pin_project_lite::pin_project! {
pub struct Catching<'a, E, F> {
ctx: &'a ExceptionContext<E>,
#[pin]
future: F,
}
}
impl<'a, E, F: core::future::Future> core::future::Future for Catching<'a, E, F> {
type Output = Result<F::Output, E>;
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
let this = self.project();
let p = this.future.poll(cx);
if let Some(exception) = this.ctx.exception.take() {
core::task::Poll::Ready(Err(exception))
} else {
p.map(Ok)
}
}
}