aws_smithy_async/future/
now_or_later.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Provides the [`NowOrLater`] future with an explicit `Now` variant
7//!
8//! When a future is immediately, ready, this enables avoiding an unnecessary allocation.
9//! This is intended to be used with `Pin<Box<dyn Future>>` or similar as the future variant. For
10//! convenience, [`BoxFuture`] is provided for this use case.
11//!
12//! Typically, this is used when creating a manual async trait. In this case, it's critical that the
13//! lifetime is captured to enable interop with the async-trait macro.
14//!
15//! # Examples
16//!
17//! ```rust
18//! mod future {
19//!   use aws_smithy_async::future::now_or_later::{NowOrLater, BoxFuture};
20//!   use std::future::Future;
21//!   pub struct ProvideRegion<'a>(NowOrLater<Option<String>, BoxFuture<'a, Option<String>>>);
22//!   impl<'a> ProvideRegion<'a> {
23//!       pub fn new(f: impl Future<Output = Option<String>> + Send + 'a) -> Self {
24//!           Self(NowOrLater::new(Box::pin(f)))
25//!       }
26//!
27//!       pub fn ready(region: Option<String>) -> Self {
28//!           Self(NowOrLater::ready(region))
29//!       }
30//!   }
31//! }
32//!
33//! pub trait ProvideRegion {
34//!     fn provide_region<'a>(&'a self) -> future::ProvideRegion<'a> where Self: 'a;
35//! }
36//!
37//! struct AsyncRegionProvider;
38//! impl AsyncRegionProvider {
39//!     async fn region(&self) -> Option<String> {
40//!         todo!()
41//!     }
42//! }
43//!
44//! impl ProvideRegion for AsyncRegionProvider {
45//!     fn provide_region<'a>(&'a self) -> future::ProvideRegion<'a> where Self: 'a {
46//!       future::ProvideRegion::new(self.region())
47//!     }
48//! }
49//! ```
50
51use std::fmt;
52use std::future::Future;
53use std::pin::Pin;
54use std::task::{Context, Poll};
55
56use pin_project_lite::pin_project;
57
58/// Boxed future type alias
59pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
60
61#[derive(Debug)]
62/// Zero sized type for using NowOrLater when no future variant exists.
63pub enum OnlyReady {}
64
65pin_project! {
66    /// Future with an explicit `Now` variant
67    ///
68    /// See the [module documentation](crate::future::now_or_later) for more information.
69    pub struct NowOrLater<T, F> {
70        #[pin]
71        inner: Inner<T, F>
72    }
73}
74
75impl<T, F> fmt::Debug for NowOrLater<T, F>
76where
77    T: fmt::Debug,
78{
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        f.debug_struct("NowOrLater")
81            .field("inner", &self.inner)
82            .finish()
83    }
84}
85
86pin_project! {
87    #[project = NowOrLaterProj]
88    enum Inner<T, F> {
89        #[non_exhaustive]
90        Now { value: Option<T> },
91        #[non_exhaustive]
92        Later { #[pin] future: F },
93    }
94}
95
96impl<T, F> fmt::Debug for Inner<T, F>
97where
98    T: fmt::Debug,
99{
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        match self {
102            Self::Now { value } => f.debug_struct("Now").field("value", value).finish(),
103            Self::Later { .. } => f
104                .debug_struct("Later")
105                .field("future", &"<future>")
106                .finish(),
107        }
108    }
109}
110
111impl<T, F> NowOrLater<T, F> {
112    /// Creates a future that will resolve when `future` resolves
113    pub fn new(future: F) -> Self {
114        Self {
115            inner: Inner::Later { future },
116        }
117    }
118
119    /// Creates a future that immediately resolves to `value`
120    pub fn ready(value: T) -> NowOrLater<T, F> {
121        let value = Some(value);
122        Self {
123            inner: Inner::Now { value },
124        }
125    }
126}
127
128impl<T, F> Future for NowOrLater<T, F>
129where
130    F: Future<Output = T>,
131{
132    type Output = T;
133
134    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
135        match self.project().inner.project() {
136            NowOrLaterProj::Now { value } => {
137                Poll::Ready(value.take().expect("cannot be called twice"))
138            }
139            NowOrLaterProj::Later { future } => future.poll(cx),
140        }
141    }
142}
143
144impl<T> Future for NowOrLater<T, OnlyReady> {
145    type Output = T;
146
147    fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
148        match self.project().inner.project() {
149            NowOrLaterProj::Now { value } => {
150                Poll::Ready(value.take().expect("cannot be called twice"))
151            }
152            NowOrLaterProj::Later { .. } => unreachable!(),
153        }
154    }
155}
156
157#[cfg(test)]
158mod test {
159    use crate::future::now_or_later::{NowOrLater, OnlyReady};
160    use futures_util::FutureExt;
161
162    #[test]
163    fn ready_future_immediately_returns() {
164        let a = true;
165        let f = if a {
166            NowOrLater::ready(5)
167        } else {
168            NowOrLater::new(async { 5 })
169        };
170        use futures_util::FutureExt;
171        assert_eq!(f.now_or_never().expect("future was ready"), 5);
172    }
173
174    #[test]
175    fn only_ready_instantiation() {
176        assert_eq!(
177            NowOrLater::<i32, OnlyReady>::ready(5)
178                .now_or_never()
179                .expect("ready"),
180            5
181        );
182    }
183
184    #[tokio::test]
185    async fn box_dyn_future() {
186        let f = async { 5 };
187        let f = Box::pin(f);
188        let wrapped = NowOrLater::new(f);
189        assert_eq!(wrapped.await, 5);
190    }
191
192    #[tokio::test]
193    async fn async_fn_future() {
194        let wrapped = NowOrLater::new(async { 5 });
195        assert_eq!(wrapped.await, 5);
196    }
197}