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 { |
9 | fn started(&self) -> bool; |
10 | } |
11 | |
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 | { |
46 | 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 | |
61 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
62 | let mut this: Projection<'_, F, R> = self.project(); |
63 | |
64 | if let InnerProj::Fut { fut: Pin<&mut R> } = this.inner.as_mut().project() { |
65 | return fut.poll(cx); |
66 | } |
67 | |
68 | match this.inner.as_mut().project_replace(replacement:Inner::Empty) { |
69 | InnerProjReplace::Init { func: F } => { |
70 | this.inner.set(Inner::Fut { fut: func() }); |
71 | if let InnerProj::Fut { fut: Pin<&mut R> } = this.inner.project() { |
72 | return fut.poll(cx); |
73 | } |
74 | unreachable!() |
75 | } |
76 | _ => unreachable!("lazy state wrong" ), |
77 | } |
78 | } |
79 | } |
80 | |