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