1 | //! Definition of the [`MaybeDone`] combinator. |
2 | |
3 | use pin_project_lite::pin_project; |
4 | use std::future::Future; |
5 | use std::pin::Pin; |
6 | use std::task::{Context, Poll}; |
7 | |
8 | pin_project! { |
9 | /// A future that may have completed. |
10 | #[derive(Debug)] |
11 | #[project = MaybeDoneProj] |
12 | #[project_replace = MaybeDoneProjReplace] |
13 | pub enum MaybeDone<Fut: Future> { |
14 | /// A not-yet-completed future. |
15 | Future { #[pin] future: Fut }, |
16 | /// The output of the completed future. |
17 | Done { output: Fut::Output }, |
18 | /// The empty variant after the result of a [`MaybeDone`] has been |
19 | /// taken using the [`take_output`](MaybeDone::take_output) method. |
20 | Gone, |
21 | } |
22 | } |
23 | |
24 | /// Wraps a future into a `MaybeDone`. |
25 | pub fn maybe_done<Fut: Future>(future: Fut) -> MaybeDone<Fut> { |
26 | MaybeDone::Future { future } |
27 | } |
28 | |
29 | impl<Fut: Future> MaybeDone<Fut> { |
30 | /// Returns an [`Option`] containing a mutable reference to the output of the future. |
31 | /// The output of this method will be [`Some`] if and only if the inner |
32 | /// future has been completed and [`take_output`](MaybeDone::take_output) |
33 | /// has not yet been called. |
34 | pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output> { |
35 | match self.project() { |
36 | MaybeDoneProj::Done { output } => Some(output), |
37 | _ => None, |
38 | } |
39 | } |
40 | |
41 | /// Attempts to take the output of a `MaybeDone` without driving it |
42 | /// towards completion. |
43 | #[inline ] |
44 | pub fn take_output(self: Pin<&mut Self>) -> Option<Fut::Output> { |
45 | match *self { |
46 | MaybeDone::Done { .. } => {} |
47 | MaybeDone::Future { .. } | MaybeDone::Gone => return None, |
48 | }; |
49 | if let MaybeDoneProjReplace::Done { output } = self.project_replace(MaybeDone::Gone) { |
50 | Some(output) |
51 | } else { |
52 | unreachable!() |
53 | } |
54 | } |
55 | } |
56 | |
57 | impl<Fut: Future> Future for MaybeDone<Fut> { |
58 | type Output = (); |
59 | |
60 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
61 | let output = match self.as_mut().project() { |
62 | MaybeDoneProj::Future { future } => ready!(future.poll(cx)), |
63 | MaybeDoneProj::Done { .. } => return Poll::Ready(()), |
64 | MaybeDoneProj::Gone => panic!("MaybeDone polled after value taken" ), |
65 | }; |
66 | self.set(MaybeDone::Done { output }); |
67 | Poll::Ready(()) |
68 | } |
69 | } |
70 | |