1 //! Definition of the MaybeDone combinator
2 
3 use core::mem;
4 use core::pin::Pin;
5 use futures_core::future::{FusedFuture, Future};
6 use futures_core::task::{Context, Poll};
7 use pin_project::{pin_project, project};
8 
9 /// A future that may have completed.
10 ///
11 /// This is created by the [`maybe_done()`] function.
12 #[pin_project]
13 #[derive(Debug)]
14 pub enum MaybeDone<Fut: Future> {
15     /// A not-yet-completed future
16     Future(#[pin] Fut),
17     /// The output of the completed future
18     Done(Fut::Output),
19     /// The empty variant after the result of a [`MaybeDone`] has been
20     /// taken using the [`take_output`](MaybeDone::take_output) method.
21     Gone,
22 }
23 
24 /// Wraps a future into a `MaybeDone`
25 ///
26 /// # Examples
27 ///
28 /// ```
29 /// # futures::executor::block_on(async {
30 /// use futures::future;
31 /// use futures::pin_mut;
32 ///
33 /// let future = future::maybe_done(async { 5 });
34 /// pin_mut!(future);
35 /// assert_eq!(future.as_mut().take_output(), None);
36 /// let () = future.as_mut().await;
37 /// assert_eq!(future.as_mut().take_output(), Some(5));
38 /// assert_eq!(future.as_mut().take_output(), None);
39 /// # });
40 /// ```
maybe_done<Fut: Future>(future: Fut) -> MaybeDone<Fut>41 pub fn maybe_done<Fut: Future>(future: Fut) -> MaybeDone<Fut> {
42     MaybeDone::Future(future)
43 }
44 
45 impl<Fut: Future> MaybeDone<Fut> {
46     /// Returns an [`Option`] containing a mutable reference to the output of the future.
47     /// The output of this method will be [`Some`] if and only if the inner
48     /// future has been completed and [`take_output`](MaybeDone::take_output)
49     /// has not yet been called.
50     #[project]
51     #[inline]
output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output>52     pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output> {
53         #[project]
54         match self.project() {
55             MaybeDone::Done(res) => Some(res),
56             _ => None,
57         }
58     }
59 
60     /// Attempt to take the output of a `MaybeDone` without driving it
61     /// towards completion.
62     #[inline]
take_output(self: Pin<&mut Self>) -> Option<Fut::Output>63     pub fn take_output(self: Pin<&mut Self>) -> Option<Fut::Output> {
64         // Safety: we return immediately unless we are in the `Done`
65         // state, which does not have any pinning guarantees to uphold.
66         //
67         // Hopefully `pin_project` will support this safely soon:
68         // https://github.com/taiki-e/pin-project/issues/184
69         unsafe {
70             let this = self.get_unchecked_mut();
71             match this {
72                 MaybeDone::Done(_) => {},
73                 MaybeDone::Future(_) | MaybeDone::Gone => return None,
74             };
75             if let MaybeDone::Done(output) = mem::replace(this, MaybeDone::Gone) {
76                 Some(output)
77             } else {
78                 unreachable!()
79             }
80         }
81     }
82 }
83 
84 impl<Fut: Future> FusedFuture for MaybeDone<Fut> {
is_terminated(&self) -> bool85     fn is_terminated(&self) -> bool {
86         match self {
87             MaybeDone::Future(_) => false,
88             MaybeDone::Done(_) | MaybeDone::Gone => true,
89         }
90     }
91 }
92 
93 impl<Fut: Future> Future for MaybeDone<Fut> {
94     type Output = ();
95 
96     #[project]
poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>97     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
98         #[project]
99         match self.as_mut().project() {
100             MaybeDone::Future(f) => {
101                 let res = ready!(f.poll(cx));
102                 self.set(MaybeDone::Done(res));
103             },
104             MaybeDone::Done(_) => {},
105             MaybeDone::Gone => panic!("MaybeDone polled after value taken"),
106         }
107         Poll::Ready(())
108     }
109 }
110