use std::{
future::{Future, IntoFuture},
pin::Pin,
};
pub trait TransposeFuture {
type Output: Future;
fn transpose(self) -> Self::Output;
}
impl<F: IntoFuture> TransposeFuture for Option<F> {
type Output = TransposedOption<F::IntoFuture>;
fn transpose(self) -> Self::Output {
TransposedOption(self.map(IntoFuture::into_future))
}
}
pub struct TransposedOption<F>(Option<F>);
impl<F: Future> Future for TransposedOption<F> {
type Output = Option<F::Output>;
fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
match unsafe { self.map_unchecked_mut(|x| &mut x.0) }.as_pin_mut() {
Some(f) => f.poll(cx).map(Some),
None => std::task::Poll::Ready(None),
}
}
}
impl<F: IntoFuture, T: Unpin> TransposeFuture for Result<F, T> {
type Output = TransposedResult<F::IntoFuture, T>;
fn transpose(self) -> Self::Output {
TransposedResult(self.map(IntoFuture::into_future).map_err(Some))
}
}
pub struct TransposedResult<F, T>(Result<F, Option<T>>);
impl<F: Future, T: Unpin> Future for TransposedResult<F, T> {
type Output = Result<F::Output, T>;
fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
let mapped = unsafe {
let x = self.get_unchecked_mut();
match &mut x.0 {
Ok(f) => Ok(Pin::new_unchecked(f)),
Err(e) => Err(Pin::new_unchecked(e)),
}
};
match mapped {
Ok(f) => f.poll(cx).map(Ok),
Err(e) => std::task::Poll::Ready(Err(e.get_mut().take().unwrap())),
}
}
}