hyper/common/
lazy.rs

1use pin_project_lite::pin_project;
2
3use super::{task, Future, Pin, Poll};
4
5pub(crate) trait Started: Future {
6    fn started(&self) -> bool;
7}
8
9pub(crate) fn lazy<F, R>(func: F) -> Lazy<F, R>
10where
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
20pin_project! {
21    #[allow(missing_debug_implementations)]
22    pub(crate) struct Lazy<F, R> {
23        #[pin]
24        inner: Inner<F, R>,
25    }
26}
27
28pin_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
38impl<F, R> Started for Lazy<F, R>
39where
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
51impl<F, R> Future for Lazy<F, R>
52where
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 = self.project();
60
61        if let InnerProj::Fut { fut } = this.inner.as_mut().project() {
62            return fut.poll(cx);
63        }
64
65        match this.inner.as_mut().project_replace(Inner::Empty) {
66            InnerProjReplace::Init { func } => {
67                this.inner.set(Inner::Fut { fut: func() });
68                if let InnerProj::Fut { fut } = this.inner.project() {
69                    return fut.poll(cx);
70                }
71                unreachable!()
72            }
73            _ => unreachable!("lazy state wrong"),
74        }
75    }
76}