1use crate::box_error::BoxError;
14use crate::client::auth::{
15 AuthScheme, AuthSchemeId, ResolveAuthSchemeOptions, SharedAuthScheme,
16 SharedAuthSchemeOptionResolver,
17};
18use crate::client::endpoint::{ResolveEndpoint, SharedEndpointResolver};
19use crate::client::http::{HttpClient, SharedHttpClient};
20use crate::client::identity::{
21 ResolveCachedIdentity, ResolveIdentity, SharedIdentityCache, SharedIdentityResolver,
22};
23use crate::client::interceptors::{Intercept, SharedInterceptor};
24use crate::client::retries::classifiers::{ClassifyRetry, SharedRetryClassifier};
25use crate::client::retries::{RetryStrategy, SharedRetryStrategy};
26use crate::impl_shared_conversions;
27use crate::shared::IntoShared;
28use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep};
29use aws_smithy_async::time::{SharedTimeSource, TimeSource};
30use aws_smithy_types::config_bag::ConfigBag;
31use std::collections::HashMap;
32use std::fmt;
33use std::sync::Arc;
34
35pub(crate) static EMPTY_RUNTIME_COMPONENTS_BUILDER: RuntimeComponentsBuilder =
36 RuntimeComponentsBuilder::new("empty");
37
38pub(crate) mod sealed {
39 use super::*;
40
41 pub trait ValidateConfig: fmt::Debug + Send + Sync {
46 #[doc = include_str!("../../rustdoc/validate_base_client_config.md")]
47 fn validate_base_client_config(
48 &self,
49 runtime_components: &RuntimeComponentsBuilder,
50 cfg: &ConfigBag,
51 ) -> Result<(), BoxError> {
52 let _ = (runtime_components, cfg);
53 Ok(())
54 }
55
56 #[doc = include_str!("../../rustdoc/validate_final_config.md")]
57 fn validate_final_config(
58 &self,
59 runtime_components: &RuntimeComponents,
60 cfg: &ConfigBag,
61 ) -> Result<(), BoxError> {
62 let _ = (runtime_components, cfg);
63 Ok(())
64 }
65 }
66}
67use sealed::ValidateConfig;
68
69#[derive(Clone)]
70enum ValidatorInner {
71 BaseConfigStaticFn(fn(&RuntimeComponentsBuilder, &ConfigBag) -> Result<(), BoxError>),
72 Shared(Arc<dyn ValidateConfig>),
73}
74
75impl fmt::Debug for ValidatorInner {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 match self {
78 Self::BaseConfigStaticFn(_) => f.debug_tuple("StaticFn").finish(),
79 Self::Shared(_) => f.debug_tuple("Shared").finish(),
80 }
81 }
82}
83
84#[derive(Clone, Debug)]
86pub struct SharedConfigValidator {
87 inner: ValidatorInner,
88}
89
90impl SharedConfigValidator {
91 pub(crate) fn new(validator: impl ValidateConfig + 'static) -> Self {
93 Self {
94 inner: ValidatorInner::Shared(Arc::new(validator) as _),
95 }
96 }
97
98 pub fn base_client_config_fn(
130 validator: fn(&RuntimeComponentsBuilder, &ConfigBag) -> Result<(), BoxError>,
131 ) -> Self {
132 Self {
133 inner: ValidatorInner::BaseConfigStaticFn(validator),
134 }
135 }
136}
137
138impl ValidateConfig for SharedConfigValidator {
139 fn validate_base_client_config(
140 &self,
141 runtime_components: &RuntimeComponentsBuilder,
142 cfg: &ConfigBag,
143 ) -> Result<(), BoxError> {
144 match &self.inner {
145 ValidatorInner::BaseConfigStaticFn(validator) => validator(runtime_components, cfg),
146 ValidatorInner::Shared(validator) => {
147 validator.validate_base_client_config(runtime_components, cfg)
148 }
149 }
150 }
151
152 fn validate_final_config(
153 &self,
154 runtime_components: &RuntimeComponents,
155 cfg: &ConfigBag,
156 ) -> Result<(), BoxError> {
157 match &self.inner {
158 ValidatorInner::Shared(validator) => {
159 validator.validate_final_config(runtime_components, cfg)
160 }
161 _ => Ok(()),
162 }
163 }
164}
165
166impl_shared_conversions!(convert SharedConfigValidator from ValidateConfig using SharedConfigValidator::new);
167
168macro_rules! merge {
172 (Option $other:ident . $name:ident => $self:ident) => {
173 $self.$name = $other.$name.clone().or($self.$name.take());
174 };
175 (Vec $other:ident . $name:ident => $self:ident) => {
176 if !$other.$name.is_empty() {
177 $self.$name.extend($other.$name.iter().cloned());
178 }
179 };
180 (OptionalAuthSchemeMap $other:ident . $name:ident => $self:ident ) => {
181 if let Some(m) = &$other.$name {
182 let mut us = $self.$name.unwrap_or_default();
183 us.extend(m.iter().map(|(k, v)| (k.clone(), v.clone())));
184 $self.$name = Some(us);
185 }
186 };
187}
188macro_rules! builder_field_value {
194 (Option $self:ident . $name:ident) => {
195 $self.$name
196 };
197 (Option $self:ident . $name:ident required) => {
198 $self.$name.ok_or(BuildError(concat!(
199 "the `",
200 stringify!($name),
201 "` runtime component is required"
202 )))?
203 };
204 (Vec $self:ident . $name:ident) => {
205 $self.$name
206 };
207 (OptionalAuthSchemeMap $self:ident . $name:ident atLeastOneRequired) => {{
208 match $self.$name {
209 Some(map) => map,
210 None => {
211 return Err(BuildError(concat!(
212 "at least one `",
213 stringify!($name),
214 "` runtime component is required"
215 )));
216 }
217 }
218 }};
219 (Vec $self:ident . $name:ident atLeastOneRequired) => {{
220 if $self.$name.is_empty() {
221 return Err(BuildError(concat!(
222 "at least one `",
223 stringify!($name),
224 "` runtime component is required"
225 )));
226 }
227 $self.$name
228 }};
229}
230macro_rules! runtime_component_field_type {
235 (Option $inner_type:ident) => {
236 Option<Tracked<$inner_type>>
237 };
238 (Option $inner_type:ident required) => {
239 Tracked<$inner_type>
240 };
241 (Vec $inner_type:ident) => {
242 Vec<Tracked<$inner_type>>
243 };
244 (Vec $inner_type:ident atLeastOneRequired) => {
245 Vec<Tracked<$inner_type>>
246 };
247 (OptionalAuthSchemeMap $inner_type: ident atLeastOneRequired) => { AuthSchemeMap<Tracked<$inner_type>> };
248}
249macro_rules! empty_builder_value {
255 (Option) => {
256 None
257 };
258 (Vec) => {
259 Vec::new()
260 };
261 (OptionalAuthSchemeMap) => {
262 None
263 };
264}
265
266type OptionalAuthSchemeMap<V> = Option<AuthSchemeMap<V>>;
267type AuthSchemeMap<V> = HashMap<AuthSchemeId, V>;
268
269macro_rules! declare_runtime_components {
301 (fields for $rc_name:ident and $builder_name:ident {
302 $($(#[$option:ident])? $field_name:ident : $outer_type:ident<$inner_type:ident> ,)+
303 }) => {
304 #[derive(Clone, Debug)]
306 pub struct $rc_name {
307 $($field_name: runtime_component_field_type!($outer_type $inner_type $($option)?),)+
308 }
309
310 #[derive(Clone, Debug)]
312 pub struct $builder_name {
313 builder_name: &'static str,
314 $($field_name: $outer_type<Tracked<$inner_type>>,)+
315 }
316 impl $builder_name {
317 pub const fn new(name: &'static str) -> Self {
323 Self {
324 builder_name: name,
325 $($field_name: empty_builder_value!($outer_type),)+
326 }
327 }
328
329 pub fn merge_from(mut self, other: &Self) -> Self {
331 $(merge!($outer_type other.$field_name => self);)+
332 self
333 }
334
335 pub fn build(self) -> Result<$rc_name, BuildError> {
337 let mut rcs = $rc_name {
338 $($field_name: builder_field_value!($outer_type self.$field_name $($option)?),)+
339 };
340 rcs.sort();
341
342 Ok(rcs)
343 }
344 }
345 };
346}
347
348declare_runtime_components! {
349 fields for RuntimeComponents and RuntimeComponentsBuilder {
350 #[required]
351 auth_scheme_option_resolver: Option<SharedAuthSchemeOptionResolver>,
352
353 http_client: Option<SharedHttpClient>,
355
356 #[required]
357 endpoint_resolver: Option<SharedEndpointResolver>,
358
359 #[atLeastOneRequired]
360 auth_schemes: Vec<SharedAuthScheme>,
361
362 #[required]
363 identity_cache: Option<SharedIdentityCache>,
364
365 #[atLeastOneRequired]
366 identity_resolvers: OptionalAuthSchemeMap<SharedIdentityResolver>,
367
368 interceptors: Vec<SharedInterceptor>,
369
370 retry_classifiers: Vec<SharedRetryClassifier>,
371
372 #[required]
373 retry_strategy: Option<SharedRetryStrategy>,
374
375 time_source: Option<SharedTimeSource>,
376
377 sleep_impl: Option<SharedAsyncSleep>,
378
379 config_validators: Vec<SharedConfigValidator>,
380 }
381}
382
383impl RuntimeComponents {
384 pub fn builder(name: &'static str) -> RuntimeComponentsBuilder {
386 RuntimeComponentsBuilder::new(name)
387 }
388
389 pub fn to_builder(&self) -> RuntimeComponentsBuilder {
391 RuntimeComponentsBuilder::from_runtime_components(
392 self.clone(),
393 "RuntimeComponentsBuilder::from_runtime_components",
394 )
395 }
396
397 pub fn auth_scheme_option_resolver(&self) -> SharedAuthSchemeOptionResolver {
399 self.auth_scheme_option_resolver.value.clone()
400 }
401
402 pub fn http_client(&self) -> Option<SharedHttpClient> {
404 self.http_client.as_ref().map(|s| s.value.clone())
405 }
406
407 pub fn endpoint_resolver(&self) -> SharedEndpointResolver {
409 self.endpoint_resolver.value.clone()
410 }
411
412 pub fn auth_scheme(&self, scheme_id: AuthSchemeId) -> Option<SharedAuthScheme> {
414 self.auth_schemes
415 .iter()
416 .find(|s| s.value.scheme_id() == scheme_id)
417 .map(|s| s.value.clone())
418 }
419
420 pub fn identity_cache(&self) -> SharedIdentityCache {
422 self.identity_cache.value.clone()
423 }
424
425 pub fn interceptors(&self) -> impl Iterator<Item = SharedInterceptor> + '_ {
427 self.interceptors.iter().map(|s| s.value.clone())
428 }
429
430 pub fn retry_classifiers(&self) -> impl Iterator<Item = SharedRetryClassifier> + '_ {
432 self.retry_classifiers.iter().map(|s| s.value.clone())
433 }
434
435 #[cfg(debug_assertions)]
437 pub(crate) fn retry_classifiers_slice(&self) -> &[Tracked<SharedRetryClassifier>] {
438 self.retry_classifiers.as_slice()
439 }
440
441 pub fn retry_strategy(&self) -> SharedRetryStrategy {
443 self.retry_strategy.value.clone()
444 }
445
446 pub fn sleep_impl(&self) -> Option<SharedAsyncSleep> {
448 self.sleep_impl.as_ref().map(|s| s.value.clone())
449 }
450
451 pub fn time_source(&self) -> Option<SharedTimeSource> {
453 self.time_source.as_ref().map(|s| s.value.clone())
454 }
455
456 pub fn config_validators(&self) -> impl Iterator<Item = SharedConfigValidator> + '_ {
458 self.config_validators.iter().map(|s| s.value.clone())
459 }
460
461 pub fn validate_final_config(&self, cfg: &ConfigBag) -> Result<(), BoxError> {
465 macro_rules! validate {
466 (Required: $field:expr) => {
467 ValidateConfig::validate_final_config(&$field.value, self, cfg)?;
468 };
469 (Option: $field:expr) => {
470 if let Some(field) = $field.as_ref() {
471 ValidateConfig::validate_final_config(&field.value, self, cfg)?;
472 }
473 };
474 (Vec: $field:expr) => {
475 for entry in $field {
476 ValidateConfig::validate_final_config(&entry.value, self, cfg)?;
477 }
478 };
479 (Map: $field:expr) => {
480 for entry in $field.values() {
481 ValidateConfig::validate_final_config(&entry.value, self, cfg)?;
482 }
483 };
484 }
485
486 for validator in self.config_validators() {
487 validator.validate_final_config(self, cfg)?;
488 }
489
490 validate!(Option: self.http_client);
491 validate!(Required: self.endpoint_resolver);
492 validate!(Vec: &self.auth_schemes);
493 validate!(Required: self.identity_cache);
494 validate!(Map: self.identity_resolvers);
495 validate!(Vec: &self.interceptors);
496 validate!(Required: self.retry_strategy);
497 validate!(Vec: &self.retry_classifiers);
498
499 Ok(())
500 }
501
502 fn sort(&mut self) {
503 self.retry_classifiers.sort_by_key(|rc| rc.value.priority());
504 }
505}
506
507impl RuntimeComponentsBuilder {
508 pub fn from_runtime_components(rc: RuntimeComponents, builder_name: &'static str) -> Self {
511 Self {
512 builder_name,
513 auth_scheme_option_resolver: Some(rc.auth_scheme_option_resolver),
514 http_client: rc.http_client,
515 endpoint_resolver: Some(rc.endpoint_resolver),
516 auth_schemes: rc.auth_schemes,
517 identity_cache: Some(rc.identity_cache),
518 identity_resolvers: Some(rc.identity_resolvers),
519 interceptors: rc.interceptors,
520 retry_classifiers: rc.retry_classifiers,
521 retry_strategy: Some(rc.retry_strategy),
522 time_source: rc.time_source,
523 sleep_impl: rc.sleep_impl,
524 config_validators: rc.config_validators,
525 }
526 }
527
528 pub fn auth_scheme_option_resolver(&self) -> Option<SharedAuthSchemeOptionResolver> {
530 self.auth_scheme_option_resolver
531 .as_ref()
532 .map(|s| s.value.clone())
533 }
534
535 pub fn set_auth_scheme_option_resolver(
537 &mut self,
538 auth_scheme_option_resolver: Option<impl ResolveAuthSchemeOptions + 'static>,
539 ) -> &mut Self {
540 self.auth_scheme_option_resolver =
541 self.tracked(auth_scheme_option_resolver.map(IntoShared::into_shared));
542 self
543 }
544
545 pub fn with_auth_scheme_option_resolver(
547 mut self,
548 auth_scheme_option_resolver: Option<impl ResolveAuthSchemeOptions + 'static>,
549 ) -> Self {
550 self.set_auth_scheme_option_resolver(auth_scheme_option_resolver);
551 self
552 }
553
554 pub fn http_client(&self) -> Option<SharedHttpClient> {
556 self.http_client.as_ref().map(|s| s.value.clone())
557 }
558
559 pub fn set_http_client(&mut self, connector: Option<impl HttpClient + 'static>) -> &mut Self {
561 self.http_client = self.tracked(connector.map(IntoShared::into_shared));
562 self
563 }
564
565 pub fn with_http_client(mut self, connector: Option<impl HttpClient + 'static>) -> Self {
567 self.set_http_client(connector);
568 self
569 }
570
571 pub fn endpoint_resolver(&self) -> Option<SharedEndpointResolver> {
573 self.endpoint_resolver.as_ref().map(|s| s.value.clone())
574 }
575
576 pub fn set_endpoint_resolver(
578 &mut self,
579 endpoint_resolver: Option<impl ResolveEndpoint + 'static>,
580 ) -> &mut Self {
581 self.endpoint_resolver =
582 endpoint_resolver.map(|s| Tracked::new(self.builder_name, s.into_shared()));
583 self
584 }
585
586 pub fn with_endpoint_resolver(
588 mut self,
589 endpoint_resolver: Option<impl ResolveEndpoint + 'static>,
590 ) -> Self {
591 self.set_endpoint_resolver(endpoint_resolver);
592 self
593 }
594
595 pub fn auth_schemes(&self) -> impl Iterator<Item = SharedAuthScheme> + '_ {
597 self.auth_schemes.iter().map(|s| s.value.clone())
598 }
599
600 pub fn push_auth_scheme(&mut self, auth_scheme: impl AuthScheme + 'static) -> &mut Self {
602 self.auth_schemes
603 .push(Tracked::new(self.builder_name, auth_scheme.into_shared()));
604 self
605 }
606
607 pub fn with_auth_scheme(mut self, auth_scheme: impl AuthScheme + 'static) -> Self {
609 self.push_auth_scheme(auth_scheme);
610 self
611 }
612
613 pub fn identity_cache(&self) -> Option<SharedIdentityCache> {
615 self.identity_cache.as_ref().map(|s| s.value.clone())
616 }
617
618 pub fn set_identity_cache(
620 &mut self,
621 identity_cache: Option<impl ResolveCachedIdentity + 'static>,
622 ) -> &mut Self {
623 self.identity_cache =
624 identity_cache.map(|c| Tracked::new(self.builder_name, c.into_shared()));
625 self
626 }
627
628 pub fn with_identity_cache(
630 mut self,
631 identity_cache: Option<impl ResolveCachedIdentity + 'static>,
632 ) -> Self {
633 self.set_identity_cache(identity_cache);
634 self
635 }
636
637 #[deprecated(
640 note = "This method is broken since it does not replace an existing identity resolver of the given auth scheme ID. Use `set_identity_resolver` instead."
641 )]
642 pub fn push_identity_resolver(
643 &mut self,
644 scheme_id: AuthSchemeId,
645 identity_resolver: impl ResolveIdentity + 'static,
646 ) -> &mut Self {
647 self.set_identity_resolver(scheme_id, identity_resolver)
648 }
649
650 pub fn set_identity_resolver(
655 &mut self,
656 scheme_id: AuthSchemeId,
657 identity_resolver: impl ResolveIdentity + 'static,
658 ) -> &mut Self {
659 let mut resolvers = self.identity_resolvers.take().unwrap_or_default();
660 resolvers.insert(
661 scheme_id,
662 Tracked::new(self.builder_name, identity_resolver.into_shared()),
663 );
664 self.identity_resolvers = Some(resolvers);
665 self
666 }
667
668 pub fn with_identity_resolver(
670 mut self,
671 scheme_id: AuthSchemeId,
672 identity_resolver: impl ResolveIdentity + 'static,
673 ) -> Self {
674 self.set_identity_resolver(scheme_id, identity_resolver);
675 self
676 }
677
678 pub fn interceptors(&self) -> impl Iterator<Item = SharedInterceptor> + '_ {
680 self.interceptors.iter().map(|s| s.value.clone())
681 }
682
683 pub fn extend_interceptors(
685 &mut self,
686 interceptors: impl Iterator<Item = SharedInterceptor>,
687 ) -> &mut Self {
688 self.interceptors
689 .extend(interceptors.map(|s| Tracked::new(self.builder_name, s)));
690 self
691 }
692
693 pub fn push_interceptor(&mut self, interceptor: impl Intercept + 'static) -> &mut Self {
695 self.interceptors
696 .push(Tracked::new(self.builder_name, interceptor.into_shared()));
697 self
698 }
699
700 pub fn with_interceptor(mut self, interceptor: impl Intercept + 'static) -> Self {
702 self.push_interceptor(interceptor);
703 self
704 }
705
706 pub fn set_interceptors(
708 &mut self,
709 interceptors: impl Iterator<Item = SharedInterceptor>,
710 ) -> &mut Self {
711 self.interceptors.clear();
712 self.interceptors
713 .extend(interceptors.map(|s| Tracked::new(self.builder_name, s)));
714 self
715 }
716
717 pub fn with_interceptors(
719 mut self,
720 interceptors: impl Iterator<Item = SharedInterceptor>,
721 ) -> Self {
722 self.set_interceptors(interceptors);
723 self
724 }
725
726 pub fn retry_classifiers(&self) -> impl Iterator<Item = SharedRetryClassifier> + '_ {
728 self.retry_classifiers.iter().map(|s| s.value.clone())
729 }
730
731 pub fn extend_retry_classifiers(
733 &mut self,
734 retry_classifiers: impl Iterator<Item = SharedRetryClassifier>,
735 ) -> &mut Self {
736 self.retry_classifiers
737 .extend(retry_classifiers.map(|s| Tracked::new(self.builder_name, s)));
738 self
739 }
740
741 pub fn push_retry_classifier(
743 &mut self,
744 retry_classifier: impl ClassifyRetry + 'static,
745 ) -> &mut Self {
746 self.retry_classifiers.push(Tracked::new(
747 self.builder_name,
748 retry_classifier.into_shared(),
749 ));
750 self
751 }
752
753 pub fn with_retry_classifier(mut self, retry_classifier: impl ClassifyRetry + 'static) -> Self {
755 self.push_retry_classifier(retry_classifier);
756 self
757 }
758
759 pub fn set_retry_classifiers(
761 &mut self,
762 retry_classifiers: impl Iterator<Item = SharedRetryClassifier>,
763 ) -> &mut Self {
764 self.retry_classifiers.clear();
765 self.retry_classifiers
766 .extend(retry_classifiers.map(|s| Tracked::new(self.builder_name, s)));
767 self
768 }
769
770 pub fn retry_strategy(&self) -> Option<SharedRetryStrategy> {
772 self.retry_strategy.as_ref().map(|s| s.value.clone())
773 }
774
775 pub fn set_retry_strategy(
777 &mut self,
778 retry_strategy: Option<impl RetryStrategy + 'static>,
779 ) -> &mut Self {
780 self.retry_strategy =
781 retry_strategy.map(|s| Tracked::new(self.builder_name, s.into_shared()));
782 self
783 }
784
785 pub fn with_retry_strategy(
787 mut self,
788 retry_strategy: Option<impl RetryStrategy + 'static>,
789 ) -> Self {
790 self.retry_strategy =
791 retry_strategy.map(|s| Tracked::new(self.builder_name, s.into_shared()));
792 self
793 }
794
795 pub fn sleep_impl(&self) -> Option<SharedAsyncSleep> {
797 self.sleep_impl.as_ref().map(|s| s.value.clone())
798 }
799
800 pub fn set_sleep_impl(&mut self, sleep_impl: Option<SharedAsyncSleep>) -> &mut Self {
802 self.sleep_impl = self.tracked(sleep_impl);
803 self
804 }
805
806 pub fn with_sleep_impl(mut self, sleep_impl: Option<impl AsyncSleep + 'static>) -> Self {
808 self.set_sleep_impl(sleep_impl.map(IntoShared::into_shared));
809 self
810 }
811
812 pub fn time_source(&self) -> Option<SharedTimeSource> {
814 self.time_source.as_ref().map(|s| s.value.clone())
815 }
816
817 pub fn set_time_source(&mut self, time_source: Option<SharedTimeSource>) -> &mut Self {
819 self.time_source = self.tracked(time_source);
820 self
821 }
822
823 pub fn with_time_source(mut self, time_source: Option<impl TimeSource + 'static>) -> Self {
825 self.set_time_source(time_source.map(IntoShared::into_shared));
826 self
827 }
828
829 pub fn config_validators(&self) -> impl Iterator<Item = SharedConfigValidator> + '_ {
831 self.config_validators.iter().map(|s| s.value.clone())
832 }
833
834 pub fn extend_config_validators(
836 &mut self,
837 config_validators: impl Iterator<Item = SharedConfigValidator>,
838 ) -> &mut Self {
839 self.config_validators
840 .extend(config_validators.map(|s| Tracked::new(self.builder_name, s)));
841 self
842 }
843
844 pub fn push_config_validator(
846 &mut self,
847 config_validator: impl ValidateConfig + 'static,
848 ) -> &mut Self {
849 self.config_validators.push(Tracked::new(
850 self.builder_name,
851 config_validator.into_shared(),
852 ));
853 self
854 }
855
856 pub fn with_config_validator(
858 mut self,
859 config_validator: impl ValidateConfig + 'static,
860 ) -> Self {
861 self.push_config_validator(config_validator);
862 self
863 }
864
865 pub fn validate_base_client_config(&self, cfg: &ConfigBag) -> Result<(), BoxError> {
869 macro_rules! validate {
870 ($field:expr) => {
871 #[allow(for_loops_over_fallibles)]
872 for entry in $field {
873 ValidateConfig::validate_base_client_config(&entry.value, self, cfg)?;
874 }
875 };
876 }
877
878 for validator in self.config_validators() {
879 validator.validate_base_client_config(self, cfg)?;
880 }
881 validate!(&self.http_client);
882 validate!(&self.endpoint_resolver);
883 validate!(&self.auth_schemes);
884 validate!(&self.identity_cache);
885 if let Some(resolvers) = &self.identity_resolvers {
886 validate!(resolvers.values())
887 }
888 validate!(&self.interceptors);
889 validate!(&self.retry_strategy);
890 Ok(())
891 }
892
893 pub fn into_time_components(mut self) -> TimeComponents {
895 TimeComponents {
896 sleep_impl: self.sleep_impl.take().map(|s| s.value),
897 time_source: self.time_source.take().map(|s| s.value),
898 }
899 }
900
901 fn tracked<T>(&self, v: Option<T>) -> Option<Tracked<T>> {
903 v.map(|v| Tracked::new(self.builder_name, v))
904 }
905}
906
907#[derive(Debug)]
909pub struct TimeComponents {
910 sleep_impl: Option<SharedAsyncSleep>,
911 time_source: Option<SharedTimeSource>,
912}
913
914impl TimeComponents {
915 pub fn sleep_impl(&self) -> Option<SharedAsyncSleep> {
917 self.sleep_impl.clone()
918 }
919
920 pub fn time_source(&self) -> Option<SharedTimeSource> {
922 self.time_source.clone()
923 }
924}
925
926#[derive(Clone, Debug)]
927#[cfg_attr(test, derive(Eq, PartialEq))]
928pub(crate) struct Tracked<T> {
929 _origin: &'static str,
930 value: T,
931}
932
933impl<T> Tracked<T> {
934 fn new(origin: &'static str, value: T) -> Self {
935 Self {
936 _origin: origin,
937 value,
938 }
939 }
940
941 #[cfg(debug_assertions)]
942 pub(crate) fn value(&self) -> &T {
943 &self.value
944 }
945}
946
947impl RuntimeComponentsBuilder {
948 #[cfg(feature = "test-util")]
950 pub fn for_tests() -> Self {
951 use crate::client::endpoint::{EndpointFuture, EndpointResolverParams};
952 use crate::client::identity::IdentityFuture;
953
954 #[derive(Debug)]
955 struct FakeAuthSchemeOptionResolver;
956 impl ResolveAuthSchemeOptions for FakeAuthSchemeOptionResolver {
957 fn resolve_auth_scheme_options(
958 &self,
959 _: &crate::client::auth::AuthSchemeOptionResolverParams,
960 ) -> Result<std::borrow::Cow<'_, [AuthSchemeId]>, BoxError> {
961 unreachable!("fake auth scheme option resolver must be overridden for this test")
962 }
963 }
964
965 #[derive(Debug)]
966 struct FakeClient;
967 impl HttpClient for FakeClient {
968 fn http_connector(
969 &self,
970 _: &crate::client::http::HttpConnectorSettings,
971 _: &RuntimeComponents,
972 ) -> crate::client::http::SharedHttpConnector {
973 unreachable!("fake client must be overridden for this test")
974 }
975 }
976
977 #[derive(Debug)]
978 struct FakeEndpointResolver;
979 impl ResolveEndpoint for FakeEndpointResolver {
980 fn resolve_endpoint<'a>(&'a self, _: &'a EndpointResolverParams) -> EndpointFuture<'a> {
981 unreachable!("fake endpoint resolver must be overridden for this test")
982 }
983 }
984
985 #[derive(Debug)]
986 struct FakeAuthScheme;
987 impl AuthScheme for FakeAuthScheme {
988 fn scheme_id(&self) -> AuthSchemeId {
989 AuthSchemeId::new("fake")
990 }
991
992 fn identity_resolver(
993 &self,
994 _: &dyn GetIdentityResolver,
995 ) -> Option<SharedIdentityResolver> {
996 None
997 }
998
999 fn signer(&self) -> &dyn crate::client::auth::Sign {
1000 unreachable!("fake http auth scheme must be overridden for this test")
1001 }
1002 }
1003
1004 #[derive(Debug)]
1005 struct FakeIdentityResolver;
1006 impl ResolveIdentity for FakeIdentityResolver {
1007 fn resolve_identity<'a>(
1008 &'a self,
1009 _: &'a RuntimeComponents,
1010 _: &'a ConfigBag,
1011 ) -> IdentityFuture<'a> {
1012 unreachable!("fake identity resolver must be overridden for this test")
1013 }
1014 }
1015
1016 #[derive(Debug)]
1017 struct FakeRetryStrategy;
1018 impl RetryStrategy for FakeRetryStrategy {
1019 fn should_attempt_initial_request(
1020 &self,
1021 _: &RuntimeComponents,
1022 _: &ConfigBag,
1023 ) -> Result<crate::client::retries::ShouldAttempt, BoxError> {
1024 unreachable!("fake retry strategy must be overridden for this test")
1025 }
1026
1027 fn should_attempt_retry(
1028 &self,
1029 _: &crate::client::interceptors::context::InterceptorContext,
1030 _: &RuntimeComponents,
1031 _: &ConfigBag,
1032 ) -> Result<crate::client::retries::ShouldAttempt, BoxError> {
1033 unreachable!("fake retry strategy must be overridden for this test")
1034 }
1035 }
1036
1037 #[derive(Debug)]
1038 struct FakeTimeSource;
1039 impl TimeSource for FakeTimeSource {
1040 fn now(&self) -> std::time::SystemTime {
1041 unreachable!("fake time source must be overridden for this test")
1042 }
1043 }
1044
1045 #[derive(Debug)]
1046 struct FakeSleep;
1047 impl AsyncSleep for FakeSleep {
1048 fn sleep(&self, _: std::time::Duration) -> aws_smithy_async::rt::sleep::Sleep {
1049 unreachable!("fake sleep must be overridden for this test")
1050 }
1051 }
1052
1053 #[derive(Debug)]
1054 struct FakeIdentityCache;
1055 impl ResolveCachedIdentity for FakeIdentityCache {
1056 fn resolve_cached_identity<'a>(
1057 &'a self,
1058 resolver: SharedIdentityResolver,
1059 components: &'a RuntimeComponents,
1060 config_bag: &'a ConfigBag,
1061 ) -> IdentityFuture<'a> {
1062 IdentityFuture::new(async move {
1063 resolver.resolve_identity(components, config_bag).await
1064 })
1065 }
1066 }
1067
1068 Self::new("aws_smithy_runtime_api::client::runtime_components::RuntimeComponentBuilder::for_tests")
1069 .with_auth_scheme(FakeAuthScheme)
1070 .with_auth_scheme_option_resolver(Some(FakeAuthSchemeOptionResolver))
1071 .with_endpoint_resolver(Some(FakeEndpointResolver))
1072 .with_http_client(Some(FakeClient))
1073 .with_identity_cache(Some(FakeIdentityCache))
1074 .with_identity_resolver(AuthSchemeId::new("fake"), FakeIdentityResolver)
1075 .with_retry_strategy(Some(FakeRetryStrategy))
1076 .with_sleep_impl(Some(SharedAsyncSleep::new(FakeSleep)))
1077 .with_time_source(Some(SharedTimeSource::new(FakeTimeSource)))
1078 }
1079}
1080
1081#[derive(Debug)]
1083pub struct BuildError(&'static str);
1084
1085impl std::error::Error for BuildError {}
1086
1087impl fmt::Display for BuildError {
1088 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1089 write!(f, "{}", self.0)
1090 }
1091}
1092
1093pub trait GetIdentityResolver: Send + Sync {
1098 fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option<SharedIdentityResolver>;
1100}
1101
1102impl GetIdentityResolver for RuntimeComponents {
1103 fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option<SharedIdentityResolver> {
1104 self.identity_resolvers
1105 .get(&scheme_id)
1106 .map(|s| s.value.clone())
1107 }
1108}
1109
1110#[cfg(all(test, feature = "test-util"))]
1111mod tests {
1112 use super::{BuildError, RuntimeComponentsBuilder, Tracked};
1113 use crate::client::runtime_components::ValidateConfig;
1114
1115 #[derive(Clone, Debug, Eq, PartialEq)]
1116 struct TestComponent(String);
1117 impl ValidateConfig for TestComponent {}
1118 impl From<&'static str> for TestComponent {
1119 fn from(value: &'static str) -> Self {
1120 TestComponent(value.into())
1121 }
1122 }
1123
1124 #[test]
1125 #[allow(unreachable_pub)]
1126 #[allow(dead_code)]
1127 fn the_builders_should_merge() {
1128 declare_runtime_components! {
1129 fields for TestRc and TestRcBuilder {
1130 #[required]
1131 some_required_component: Option<TestComponent>,
1132
1133 some_optional_component: Option<TestComponent>,
1134
1135 #[atLeastOneRequired]
1136 some_required_vec: Vec<TestComponent>,
1137
1138 some_optional_vec: Vec<TestComponent>,
1139 }
1140 }
1141
1142 impl TestRc {
1143 fn sort(&mut self) {}
1144 }
1145
1146 let builder1 = TestRcBuilder {
1147 builder_name: "builder1",
1148 some_required_component: Some(Tracked::new("builder1", "override_me".into())),
1149 some_optional_component: Some(Tracked::new("builder1", "override_me optional".into())),
1150 some_required_vec: vec![Tracked::new("builder1", "first".into())],
1151 some_optional_vec: vec![Tracked::new("builder1", "first optional".into())],
1152 };
1153 let builder2 = TestRcBuilder {
1154 builder_name: "builder2",
1155 some_required_component: Some(Tracked::new("builder2", "override_me_too".into())),
1156 some_optional_component: Some(Tracked::new(
1157 "builder2",
1158 "override_me_too optional".into(),
1159 )),
1160 some_required_vec: vec![Tracked::new("builder2", "second".into())],
1161 some_optional_vec: vec![Tracked::new("builder2", "second optional".into())],
1162 };
1163 let builder3 = TestRcBuilder {
1164 builder_name: "builder3",
1165 some_required_component: Some(Tracked::new("builder3", "correct".into())),
1166 some_optional_component: Some(Tracked::new("builder3", "correct optional".into())),
1167 some_required_vec: vec![Tracked::new("builder3", "third".into())],
1168 some_optional_vec: vec![Tracked::new("builder3", "third optional".into())],
1169 };
1170 let rc = TestRcBuilder::new("root")
1171 .merge_from(&builder1)
1172 .merge_from(&builder2)
1173 .merge_from(&builder3)
1174 .build()
1175 .expect("success");
1176 assert_eq!(
1177 Tracked::new("builder3", TestComponent::from("correct")),
1178 rc.some_required_component
1179 );
1180 assert_eq!(
1181 Some(Tracked::new(
1182 "builder3",
1183 TestComponent::from("correct optional")
1184 )),
1185 rc.some_optional_component
1186 );
1187 assert_eq!(
1188 vec![
1189 Tracked::new("builder1", TestComponent::from("first")),
1190 Tracked::new("builder2", TestComponent::from("second")),
1191 Tracked::new("builder3", TestComponent::from("third"))
1192 ],
1193 rc.some_required_vec
1194 );
1195 assert_eq!(
1196 vec![
1197 Tracked::new("builder1", TestComponent::from("first optional")),
1198 Tracked::new("builder2", TestComponent::from("second optional")),
1199 Tracked::new("builder3", TestComponent::from("third optional"))
1200 ],
1201 rc.some_optional_vec
1202 );
1203 }
1204
1205 #[test]
1206 #[allow(unreachable_pub)]
1207 #[allow(dead_code)]
1208 #[should_panic(expected = "the `_some_component` runtime component is required")]
1209 fn require_field_singular() {
1210 declare_runtime_components! {
1211 fields for TestRc and TestRcBuilder {
1212 #[required]
1213 _some_component: Option<TestComponent>,
1214 }
1215 }
1216
1217 impl TestRc {
1218 fn sort(&mut self) {}
1219 }
1220
1221 let rc = TestRcBuilder::new("test").build().unwrap();
1222
1223 let _: Tracked<TestComponent> = rc._some_component;
1225 }
1226
1227 #[test]
1228 #[allow(unreachable_pub)]
1229 #[allow(dead_code)]
1230 #[should_panic(expected = "at least one `_some_vec` runtime component is required")]
1231 fn require_field_plural() {
1232 declare_runtime_components! {
1233 fields for TestRc and TestRcBuilder {
1234 #[atLeastOneRequired]
1235 _some_vec: Vec<TestComponent>,
1236 }
1237 }
1238
1239 impl TestRc {
1240 fn sort(&mut self) {}
1241 }
1242
1243 let rc = TestRcBuilder::new("test").build().unwrap();
1244
1245 let _: Vec<Tracked<TestComponent>> = rc._some_vec;
1247 }
1248
1249 #[test]
1250 #[allow(unreachable_pub)]
1251 #[allow(dead_code)]
1252 fn optional_fields_dont_panic() {
1253 declare_runtime_components! {
1254 fields for TestRc and TestRcBuilder {
1255 _some_optional_component: Option<TestComponent>,
1256 _some_optional_vec: Vec<TestComponent>,
1257 }
1258 }
1259
1260 impl TestRc {
1261 fn sort(&mut self) {}
1262 }
1263
1264 let rc = TestRcBuilder::new("test").build().unwrap();
1265
1266 let _: Option<Tracked<TestComponent>> = rc._some_optional_component;
1268 let _: Vec<Tracked<TestComponent>> = rc._some_optional_vec;
1269 }
1270
1271 #[test]
1272 fn building_test_builder_should_not_panic() {
1273 let _ = RuntimeComponentsBuilder::for_tests().build(); }
1275
1276 #[test]
1277 fn set_identity_resolver_should_replace_existing_resolver_for_given_auth_scheme() {
1278 use crate::client::auth::AuthSchemeId;
1279 use crate::client::identity::{Identity, IdentityFuture, ResolveIdentity};
1280 use crate::client::runtime_components::{GetIdentityResolver, RuntimeComponents};
1281 use aws_smithy_types::config_bag::ConfigBag;
1282 use tokio::runtime::Runtime;
1283
1284 #[derive(Debug)]
1285 struct AnotherFakeIdentityResolver;
1286 impl ResolveIdentity for AnotherFakeIdentityResolver {
1287 fn resolve_identity<'a>(
1288 &'a self,
1289 _: &'a RuntimeComponents,
1290 _: &'a ConfigBag,
1291 ) -> IdentityFuture<'a> {
1292 IdentityFuture::ready(Ok(Identity::new("doesn't matter", None)))
1293 }
1294 }
1295
1296 let rc = RuntimeComponentsBuilder::for_tests()
1299 .with_identity_resolver(AuthSchemeId::new("fake"), AnotherFakeIdentityResolver)
1300 .build()
1301 .expect("should build RuntimeComponents");
1302
1303 let resolver = rc
1304 .identity_resolver(AuthSchemeId::new("fake"))
1305 .expect("identity resolver should be found");
1306
1307 let identity = Runtime::new().unwrap().block_on(async {
1308 resolver
1309 .resolve_identity(&rc, &ConfigBag::base())
1310 .await
1311 .expect("identity should be resolved")
1312 });
1313
1314 assert_eq!(Some(&"doesn't matter"), identity.data::<&str>());
1315 }
1316}