Skip to main content

hyper_util/common/
lazy.rs

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