1use std::future::Future;
2use std::marker::Unpin;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5
6use pin_project_lite::pin_project;
7
8pub(crate) trait Started: Future {
9 fn started(&self) -> bool;
10}
11
12pub(crate) fn lazy<F, R>(func: F) -> Lazy<F, R>
13where
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
23pin_project! {
24 #[allow(missing_debug_implementations)]
25 pub(crate) struct Lazy<F, R> {
26 #[pin]
27 inner: Inner<F, R>,
28 }
29}
30
31pin_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
41impl<F, R> Started for Lazy<F, R>
42where
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
54impl<F, R> Future for Lazy<F, R>
55where
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