Skip to content

Commit 842ee41

Browse files
committed
Add ability to map boxed future in new_type_future
1 parent fd9ab82 commit 842ee41

File tree

4 files changed

+201
-3
lines changed

4 files changed

+201
-3
lines changed

rust-runtime/aws-smithy-async/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "aws-smithy-async"
3-
version = "1.2.5"
3+
version = "1.3.0"
44
authors = ["AWS Rust SDK Team <[email protected]>", "John DiSanti <[email protected]>"]
55
description = "Async runtime agnostic abstractions for smithy-rs."
66
edition = "2021"

rust-runtime/aws-smithy-async/src/future/now_or_later.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,66 @@ impl<T, F> NowOrLater<T, F> {
125125
}
126126
}
127127

128+
impl<'a, T, F> NowOrLater<T, F>
129+
where
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+
170+
impl<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+
128188
impl<T, F> Future for NowOrLater<T, F>
129189
where
130190
F: Future<Output = T>,
@@ -194,4 +254,18 @@ mod test {
194254
let wrapped = NowOrLater::new(async { 5 });
195255
assert_eq!(wrapped.await, 5);
196256
}
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+
}
197271
}

rust-runtime/aws-smithy-runtime-api/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "aws-smithy-runtime-api"
3-
version = "1.8.3"
3+
version = "1.8.4"
44
authors = ["AWS Rust SDK Team <[email protected]>", "Zelda Hessler <[email protected]>"]
55
description = "Smithy runtime types."
66
edition = "2021"

rust-runtime/aws-smithy-runtime-api/src/client/auth.rs

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Storable, StoreReplac
1515
use aws_smithy_types::type_erasure::TypeErasedBox;
1616
use aws_smithy_types::Document;
1717
use std::borrow::Cow;
18-
use std::fmt;
18+
use std::fmt::{self, Debug};
1919
use std::sync::Arc;
2020

2121
/// Auth schemes for the HTTP `Authorization` header.
@@ -225,6 +225,25 @@ new_type_future! {
225225
pub struct AuthSchemeOptionsFuture<'a, Vec<AuthSchemeOption>, BoxError>;
226226
}
227227

228+
// Currently, we don't add `map_ok` to the `new_type_future` macro in general.
229+
// It's specifically used for `AuthSchemeOptionsFuture`, but we can expand it later if needed.
230+
impl<'a> AuthSchemeOptionsFuture<'a> {
231+
/// Transforms the `Ok` variant inside this `AuthSchemeOptionsFuture` by applying the provided function.
232+
///
233+
/// This method maps over the `Ok` variant of the `Result` wrapped by the future,
234+
/// applying `map_fn` to the contained `Vec<AuthSchemeOption>`.
235+
///
236+
/// The transformation is applied regardless of whether the future's value is already
237+
/// available (`Now`) or will be computed asynchronously (`Later`).
238+
pub fn map_ok<F>(self, f: F) -> AuthSchemeOptionsFuture<'a>
239+
where
240+
F: FnOnce(Vec<AuthSchemeOption>) -> Vec<AuthSchemeOption> + Send + 'a,
241+
{
242+
let inner = self.inner.map_boxed(|result| result.map(f));
243+
Self { inner }
244+
}
245+
}
246+
228247
/// Resolver for auth scheme options.
229248
///
230249
/// The orchestrator needs to select an auth scheme to sign requests with, and potentially
@@ -420,3 +439,108 @@ impl<'a> From<&'a Document> for AuthSchemeEndpointConfig<'a> {
420439
Self(Some(value))
421440
}
422441
}
442+
443+
/// An ordered list of [AuthSchemeId]s
444+
///
445+
/// Can be used to reorder already-resolved auth schemes by an auth scheme resolver.
446+
/// This list is intended as a hint rather than a strict override;
447+
/// any schemes not present in the resolved auth schemes will be ignored.
448+
#[derive(Clone, Debug, Default, Eq, PartialEq)]
449+
pub struct AuthSchemePreference {
450+
preference_list: Vec<AuthSchemeId>,
451+
}
452+
453+
impl Storable for AuthSchemePreference {
454+
type Storer = StoreReplace<Self>;
455+
}
456+
457+
impl IntoIterator for AuthSchemePreference {
458+
type Item = AuthSchemeId;
459+
type IntoIter = std::vec::IntoIter<Self::Item>;
460+
461+
fn into_iter(self) -> Self::IntoIter {
462+
self.preference_list.into_iter()
463+
}
464+
}
465+
466+
impl<T> From<T> for AuthSchemePreference
467+
where
468+
T: AsRef<[AuthSchemeId]>,
469+
{
470+
fn from(slice: T) -> Self {
471+
AuthSchemePreference {
472+
preference_list: slice.as_ref().to_vec(),
473+
}
474+
}
475+
}
476+
477+
#[cfg(test)]
478+
mod tests {
479+
use super::*;
480+
481+
#[tokio::test]
482+
async fn test_map_ok_now_variant() {
483+
let input = ["sigv4", "http"]
484+
.map(|s| AuthSchemeOption::from(AuthSchemeId::from(s)))
485+
.to_vec();
486+
let fut = AuthSchemeOptionsFuture::ready(Ok(input));
487+
488+
let mapped = fut.map_ok(|opts| {
489+
opts.into_iter()
490+
.filter(|opt| opt.scheme_id().inner() != "http")
491+
.collect()
492+
});
493+
494+
let result = mapped.await;
495+
assert!(result.is_ok());
496+
let vec = result.unwrap();
497+
assert_eq!(1, vec.len());
498+
assert_eq!("sigv4", vec[0].scheme_id().inner());
499+
}
500+
501+
#[tokio::test]
502+
async fn test_map_ok_now_variant_error_no_op() {
503+
let fut = AuthSchemeOptionsFuture::ready(Err(BoxError::from("oops")));
504+
505+
let mapped = fut.map_ok(|opts| opts); // no-op
506+
507+
let result = mapped.await;
508+
assert!(result.is_err());
509+
assert_eq!(result.unwrap_err().to_string(), "oops");
510+
}
511+
512+
#[tokio::test]
513+
async fn test_map_ok_later_variant() {
514+
let input = ["foo", "bar"]
515+
.map(|s| AuthSchemeOption::from(AuthSchemeId::from(s)))
516+
.to_vec();
517+
let fut = AuthSchemeOptionsFuture::new(async move { Ok(input) });
518+
519+
let mapped = fut.map_ok(|opts| {
520+
opts.into_iter()
521+
.map(|mut opt| {
522+
opt.scheme_id =
523+
AuthSchemeId::from(Cow::Owned(opt.scheme_id().inner().to_uppercase()));
524+
opt
525+
})
526+
.collect()
527+
});
528+
529+
let result = mapped.await;
530+
assert!(result.is_ok());
531+
let vec = result.unwrap();
532+
assert_eq!(vec[0].scheme_id().inner(), "FOO");
533+
assert_eq!(vec[1].scheme_id().inner(), "BAR");
534+
}
535+
536+
#[tokio::test]
537+
async fn test_map_ok_later_variant_error_no_op() {
538+
let fut = AuthSchemeOptionsFuture::new(async move { Err(BoxError::from("later fail")) });
539+
540+
let mapped = fut.map_ok(|opts| opts); // no-op
541+
542+
let result = mapped.await;
543+
assert!(result.is_err());
544+
assert_eq!(result.unwrap_err().to_string(), "later fail");
545+
}
546+
}

0 commit comments

Comments
 (0)