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<'a, T, F> NowOrLater<T, F>
129where
130    F: Future<Output = T> + Send + 'a,
131{
132    /// Maps the value inside `NowOrLater` and returns a boxed future with lifetime `'a`.
133    ///
134    /// Examples
135    ///
136    /// ```no_run
137    /// # use aws_smithy_async::future::now_or_later::{NowOrLater, BoxFuture};
138    /// # async fn map_boxed_later_variant() {
139    /// let later = NowOrLater::new(async { 10 });
140    /// let mapped = later.map_boxed(|x| x + 5);
141    /// assert_eq!(15, mapped.await);
142    /// # }
143    /// ```
144    pub fn map_boxed<U, M>(self, map_fn: M) -> NowOrLater<U, BoxFuture<'a, U>>
145    where
146        M: FnOnce(T) -> U + Send + 'a,
147    {
148        match self.inner {
149            Inner::Now { value } => {
150                let mapped = value.map(map_fn);
151                NowOrLater {
152                    inner: Inner::Now { value: mapped },
153                }
154            }
155            Inner::Later { future } => {
156                let fut = async move {
157                    let val = future.await;
158                    map_fn(val)
159                };
160                NowOrLater {
161                    inner: Inner::Later {
162                        future: Box::pin(fut),
163                    },
164                }
165            }
166        }
167    }
168}
169
170impl<T> NowOrLater<T, OnlyReady> {
171    /// Like [`NowOrLater::map_boxed`], but specialized for use with [`OnlyReady`]
172    pub fn map_boxed<U, M>(self, map_fn: M) -> NowOrLater<U, OnlyReady>
173    where
174        M: FnOnce(T) -> U,
175    {
176        match self.inner {
177            Inner::Now { value } => {
178                let mapped = value.map(map_fn);
179                NowOrLater {
180                    inner: Inner::Now { value: mapped },
181                }
182            }
183            Inner::Later { .. } => unreachable!(),
184        }
185    }
186}
187
188impl<T, F> Future for NowOrLater<T, F>
189where
190    F: Future<Output = T>,
191{
192    type Output = T;
193
194    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
195        match self.project().inner.project() {
196            NowOrLaterProj::Now { value } => {
197                Poll::Ready(value.take().expect("cannot be called twice"))
198            }
199            NowOrLaterProj::Later { future } => future.poll(cx),
200        }
201    }
202}
203
204impl<T> Future for NowOrLater<T, OnlyReady> {
205    type Output = T;
206
207    fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
208        match self.project().inner.project() {
209            NowOrLaterProj::Now { value } => {
210                Poll::Ready(value.take().expect("cannot be called twice"))
211            }
212            NowOrLaterProj::Later { .. } => unreachable!(),
213        }
214    }
215}
216
217#[cfg(test)]
218mod test {
219    use crate::future::now_or_later::{NowOrLater, OnlyReady};
220    use futures_util::FutureExt;
221
222    #[test]
223    fn ready_future_immediately_returns() {
224        let a = true;
225        let f = if a {
226            NowOrLater::ready(5)
227        } else {
228            NowOrLater::new(async { 5 })
229        };
230        use futures_util::FutureExt;
231        assert_eq!(f.now_or_never().expect("future was ready"), 5);
232    }
233
234    #[test]
235    fn only_ready_instantiation() {
236        assert_eq!(
237            NowOrLater::<i32, OnlyReady>::ready(5)
238                .now_or_never()
239                .expect("ready"),
240            5
241        );
242    }
243
244    #[tokio::test]
245    async fn box_dyn_future() {
246        let f = async { 5 };
247        let f = Box::pin(f);
248        let wrapped = NowOrLater::new(f);
249        assert_eq!(wrapped.await, 5);
250    }
251
252    #[tokio::test]
253    async fn async_fn_future() {
254        let wrapped = NowOrLater::new(async { 5 });
255        assert_eq!(wrapped.await, 5);
256    }
257
258    #[tokio::test]
259    async fn map_boxed_now_variant() {
260        let now: NowOrLater<i32, OnlyReady> = NowOrLater::ready(21);
261        let mapped = now.map_boxed(|x| x * 2);
262        assert_eq!(42, mapped.await);
263    }
264
265    #[tokio::test]
266    async fn map_boxed_later_variant() {
267        let later = NowOrLater::new(async { 10 });
268        let mapped = later.map_boxed(|x| x + 5);
269        assert_eq!(15, mapped.await);
270    }
271}