@@ -15,7 +15,7 @@ use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Storable, StoreReplac
15
15
use aws_smithy_types:: type_erasure:: TypeErasedBox ;
16
16
use aws_smithy_types:: Document ;
17
17
use std:: borrow:: Cow ;
18
- use std:: fmt;
18
+ use std:: fmt:: { self , Debug } ;
19
19
use std:: sync:: Arc ;
20
20
21
21
/// Auth schemes for the HTTP `Authorization` header.
@@ -225,6 +225,25 @@ new_type_future! {
225
225
pub struct AuthSchemeOptionsFuture <' a, Vec <AuthSchemeOption >, BoxError >;
226
226
}
227
227
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
+
228
247
/// Resolver for auth scheme options.
229
248
///
230
249
/// 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> {
420
439
Self ( Some ( value) )
421
440
}
422
441
}
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