hyper/common/
lazy.rs

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 = self.project();
63
64        if let InnerProj::Fut { fut } = this.inner.as_mut().project() {
65            return fut.poll(cx);
66        }
67
68        match this.inner.as_mut().project_replace(Inner::Empty) {
69            InnerProjReplace::Init { func } => {
70                this.inner.set(Inner::Fut { fut: func() });
71                if let InnerProj::Fut { fut } = this.inner.project() {
72                    return fut.poll(cx);
73                }
74                unreachable!()
75            }
76            _ => unreachable!("lazy state wrong"),
77        }
78    }
79}