1 use std::future::Future;
2 use std::marker::Unpin;
3 use std::pin::Pin;
4 use std::task::{Context, Poll};
5 
6 use pin_project_lite::pin_project;
7 
8 pub(crate) trait Started: Future {
started(&self) -> bool9     fn started(&self) -> bool;
10 }
11 
lazy<F, R>(func: F) -> Lazy<F, R> where F: FnOnce() -> R, R: Future + Unpin,12 pub(crate) fn lazy<F, R>(func: F) -> Lazy<F, R>
13 where
14     F: FnOnce() -> R,
15     R: Future + Unpin,
16 {
17     Lazy {
18         inner: Inner::Init { func },
19     }
20 }
21 
22 // FIXME: allow() required due to `impl Trait` leaking types to this lint
23 pin_project! {
24     #[allow(missing_debug_implementations)]
25     pub(crate) struct Lazy<F, R> {
26         #[pin]
27         inner: Inner<F, R>,
28     }
29 }
30 
31 pin_project! {
32     #[project = InnerProj]
33     #[project_replace = InnerProjReplace]
34     enum Inner<F, R> {
35         Init { func: F },
36         Fut { #[pin] fut: R },
37         Empty,
38     }
39 }
40 
41 impl<F, R> Started for Lazy<F, R>
42 where
43     F: FnOnce() -> R,
44     R: Future,
45 {
started(&self) -> bool46     fn started(&self) -> bool {
47         match self.inner {
48             Inner::Init { .. } => false,
49             Inner::Fut { .. } | Inner::Empty => true,
50         }
51     }
52 }
53 
54 impl<F, R> Future for Lazy<F, R>
55 where
56     F: FnOnce() -> R,
57     R: Future,
58 {
59     type Output = R::Output;
60 
poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>61     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
62         let mut this = self.project();
63 
64         if let InnerProj::Fut { fut } = this.inner.as_mut().project() {
65             return fut.poll(cx);
66         }
67 
68         match this.inner.as_mut().project_replace(Inner::Empty) {
69             InnerProjReplace::Init { func } => {
70                 this.inner.set(Inner::Fut { fut: func() });
71                 if let InnerProj::Fut { fut } = this.inner.project() {
72                     return fut.poll(cx);
73                 }
74                 unreachable!()
75             }
76             _ => unreachable!("lazy state wrong"),
77         }
78     }
79 }
80