transpose_future/
lib.rs

1//! Transpose Result/Option of Future to Future of Result/Option.
2//!
3//! For collections of multiple elements use join instead.
4//!
5//! ```
6//! # use transpose_future::TransposeFuture;
7//! # async fn m() {
8//! let x: Option<i32> = Some(async {5}).transpose().await;
9//! # }
10//! ```
11use std::{
12    future::{Future, IntoFuture},
13    pin::Pin,
14};
15
16pub trait TransposeFuture {
17    type Output: Future;
18    fn transpose(self) -> Self::Output;
19}
20impl<F: IntoFuture> TransposeFuture for Option<F> {
21    type Output = TransposedOption<F::IntoFuture>;
22    /// Transpose an Option<impl Future<Output = T>> to an impl Future<Output = Option<T>>
23    ///
24    /// ```
25    /// # use transpose_future::TransposeFuture;
26    /// # async fn m() {
27    /// let x: Option<i32> = Some(async {5}).transpose().await;
28    /// # }
29    /// ```
30    fn transpose(self) -> Self::Output {
31        TransposedOption(self.map(IntoFuture::into_future))
32    }
33}
34pub struct TransposedOption<F>(Option<F>);
35impl<F: Future> Future for TransposedOption<F> {
36    type Output = Option<F::Output>;
37    fn poll(
38        self: std::pin::Pin<&mut Self>,
39        cx: &mut std::task::Context<'_>,
40    ) -> std::task::Poll<Self::Output> {
41        // SAFETY: We do not move here, just get a reference to the inner value. There is no other data.
42        match unsafe { self.map_unchecked_mut(|x| &mut x.0) }.as_pin_mut() {
43            Some(f) => f.poll(cx).map(Some),
44            None => std::task::Poll::Ready(None),
45        }
46    }
47}
48impl<F: IntoFuture, T: Unpin> TransposeFuture for Result<F, T> {
49    type Output = TransposedResult<F::IntoFuture, T>;
50    /// Transpose an Result<impl Future<Output = T>, E> to an impl Future<Output = Result<T, E>>
51    ///
52    /// ```
53    /// # use transpose_future::TransposeFuture;
54    /// # async fn m() {
55    /// let x: Result<i32, ()> = Ok(async {5}).transpose().await;
56    /// # }
57    /// ```
58    fn transpose(self) -> Self::Output {
59        TransposedResult(self.map(IntoFuture::into_future).map_err(Some))
60    }
61}
62pub struct TransposedResult<F, T>(Result<F, Option<T>>);
63impl<F: Future, T: Unpin> Future for TransposedResult<F, T> {
64    type Output = Result<F::Output, T>;
65    fn poll(
66        self: std::pin::Pin<&mut Self>,
67        cx: &mut std::task::Context<'_>,
68    ) -> std::task::Poll<Self::Output> {
69        // SAFETY: We do not move anything here, we essentially do as_pin_mut on the inner value.
70        let mapped = unsafe {
71            let x = self.get_unchecked_mut();
72            match &mut x.0 {
73                Ok(f) => Ok(Pin::new_unchecked(f)),
74                Err(e) => Err(Pin::new_unchecked(e)),
75            }
76        };
77        match mapped {
78            Ok(f) => f.poll(cx).map(Ok),
79            Err(e) => std::task::Poll::Ready(Err(e.get_mut().take().unwrap())),
80        }
81    }
82}