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: OptionalAuthSchemeMap<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: impl AsRef<AuthSchemeId>) -> Option<SharedAuthScheme> {
414 self.auth_schemes
415 .get(scheme_id.as_ref())
416 .map(|s| s.value.clone())
417 }
418
419 pub fn identity_cache(&self) -> SharedIdentityCache {
421 self.identity_cache.value.clone()
422 }
423
424 pub fn interceptors(&self) -> impl Iterator<Item = SharedInterceptor> + '_ {
426 self.interceptors.iter().map(|s| s.value.clone())
427 }
428
429 pub fn retry_classifiers(&self) -> impl Iterator<Item = SharedRetryClassifier> + '_ {
431 self.retry_classifiers.iter().map(|s| s.value.clone())
432 }
433
434 #[cfg(debug_assertions)]
436 pub(crate) fn retry_classifiers_slice(&self) -> &[Tracked<SharedRetryClassifier>] {
437 self.retry_classifiers.as_slice()
438 }
439
440 pub fn retry_strategy(&self) -> SharedRetryStrategy {
442 self.retry_strategy.value.clone()
443 }
444
445 pub fn sleep_impl(&self) -> Option<SharedAsyncSleep> {
447 self.sleep_impl.as_ref().map(|s| s.value.clone())
448 }
449
450 pub fn time_source(&self) -> Option<SharedTimeSource> {
452 self.time_source.as_ref().map(|s| s.value.clone())
453 }
454
455 pub fn config_validators(&self) -> impl Iterator<Item = SharedConfigValidator> + '_ {
457 self.config_validators.iter().map(|s| s.value.clone())
458 }
459
460 pub fn validate_final_config(&self, cfg: &ConfigBag) -> Result<(), BoxError> {
464 macro_rules! validate {
465 (Required: $field:expr) => {
466 ValidateConfig::validate_final_config(&$field.value, self, cfg)?;
467 };
468 (Option: $field:expr) => {
469 if let Some(field) = $field.as_ref() {
470 ValidateConfig::validate_final_config(&field.value, self, cfg)?;
471 }
472 };
473 (Vec: $field:expr) => {
474 for entry in $field {
475 ValidateConfig::validate_final_config(&entry.value, self, cfg)?;
476 }
477 };
478 (Map: $field:expr) => {
479 for entry in $field.values() {
480 ValidateConfig::validate_final_config(&entry.value, self, cfg)?;
481 }
482 };
483 }
484
485 for validator in self.config_validators() {
486 validator.validate_final_config(self, cfg)?;
487 }
488
489 validate!(Option: self.http_client);
490 validate!(Required: self.endpoint_resolver);
491 validate!(Map: &self.auth_schemes);
492 validate!(Required: self.identity_cache);
493 validate!(Map: self.identity_resolvers);
494 validate!(Vec: &self.interceptors);
495 validate!(Required: self.retry_strategy);
496 validate!(Vec: &self.retry_classifiers);
497
498 Ok(())
499 }
500
501 fn sort(&mut self) {
502 self.retry_classifiers.sort_by_key(|rc| rc.value.priority());
503 }
504}
505
506impl RuntimeComponentsBuilder {
507 pub fn from_runtime_components(rc: RuntimeComponents, builder_name: &'static str) -> Self {
510 Self {
511 builder_name,
512 auth_scheme_option_resolver: Some(rc.auth_scheme_option_resolver),
513 http_client: rc.http_client,
514 endpoint_resolver: Some(rc.endpoint_resolver),
515 auth_schemes: Some(rc.auth_schemes),
516 identity_cache: Some(rc.identity_cache),
517 identity_resolvers: Some(rc.identity_resolvers),
518 interceptors: rc.interceptors,
519 retry_classifiers: rc.retry_classifiers,
520 retry_strategy: Some(rc.retry_strategy),
521 time_source: rc.time_source,
522 sleep_impl: rc.sleep_impl,
523 config_validators: rc.config_validators,
524 }
525 }
526
527 pub fn auth_scheme_option_resolver(&self) -> Option<SharedAuthSchemeOptionResolver> {
529 self.auth_scheme_option_resolver
530 .as_ref()
531 .map(|s| s.value.clone())
532 }
533
534 pub fn set_auth_scheme_option_resolver(
536 &mut self,
537 auth_scheme_option_resolver: Option<impl ResolveAuthSchemeOptions + 'static>,
538 ) -> &mut Self {
539 self.auth_scheme_option_resolver =
540 self.tracked(auth_scheme_option_resolver.map(IntoShared::into_shared));
541 self
542 }
543
544 pub fn with_auth_scheme_option_resolver(
546 mut self,
547 auth_scheme_option_resolver: Option<impl ResolveAuthSchemeOptions + 'static>,
548 ) -> Self {
549 self.set_auth_scheme_option_resolver(auth_scheme_option_resolver);
550 self
551 }
552
553 pub fn http_client(&self) -> Option<SharedHttpClient> {
555 self.http_client.as_ref().map(|s| s.value.clone())
556 }
557
558 pub fn set_http_client(&mut self, connector: Option<impl HttpClient + 'static>) -> &mut Self {
560 self.http_client = self.tracked(connector.map(IntoShared::into_shared));
561 self
562 }
563
564 pub fn with_http_client(mut self, connector: Option<impl HttpClient + 'static>) -> Self {
566 self.set_http_client(connector);
567 self
568 }
569
570 pub fn endpoint_resolver(&self) -> Option<SharedEndpointResolver> {
572 self.endpoint_resolver.as_ref().map(|s| s.value.clone())
573 }
574
575 pub fn set_endpoint_resolver(
577 &mut self,
578 endpoint_resolver: Option<impl ResolveEndpoint + 'static>,
579 ) -> &mut Self {
580 self.endpoint_resolver =
581 endpoint_resolver.map(|s| Tracked::new(self.builder_name, s.into_shared()));
582 self
583 }
584
585 pub fn with_endpoint_resolver(
587 mut self,
588 endpoint_resolver: Option<impl ResolveEndpoint + 'static>,
589 ) -> Self {
590 self.set_endpoint_resolver(endpoint_resolver);
591 self
592 }
593
594 pub fn auth_schemes(&self) -> impl Iterator<Item = SharedAuthScheme> + '_ {
596 self.auth_schemes
597 .iter()
598 .flat_map(|s| s.values().map(|t| t.value.clone()))
599 }
600
601 pub fn push_auth_scheme(&mut self, auth_scheme: impl AuthScheme + 'static) -> &mut Self {
603 let mut auth_schemes = self.auth_schemes.take().unwrap_or_default();
604 auth_schemes.insert(
605 auth_scheme.scheme_id(),
606 Tracked::new(self.builder_name, auth_scheme.into_shared()),
607 );
608 self.auth_schemes = Some(auth_schemes);
609 self
610 }
611
612 pub fn with_auth_scheme(mut self, auth_scheme: impl AuthScheme + 'static) -> Self {
614 self.push_auth_scheme(auth_scheme);
615 self
616 }
617
618 pub fn identity_cache(&self) -> Option<SharedIdentityCache> {
620 self.identity_cache.as_ref().map(|s| s.value.clone())
621 }
622
623 pub fn set_identity_cache(
625 &mut self,
626 identity_cache: Option<impl ResolveCachedIdentity + 'static>,
627 ) -> &mut Self {
628 self.identity_cache =
629 identity_cache.map(|c| Tracked::new(self.builder_name, c.into_shared()));
630 self
631 }
632
633 pub fn with_identity_cache(
635 mut self,
636 identity_cache: Option<impl ResolveCachedIdentity + 'static>,
637 ) -> Self {
638 self.set_identity_cache(identity_cache);
639 self
640 }
641
642 pub fn identity_resolver(&self, scheme_id: &AuthSchemeId) -> Option<SharedIdentityResolver> {
644 self.identity_resolvers
645 .as_ref()
646 .and_then(|resolvers| resolvers.get(scheme_id))
647 .map(|tracked| tracked.value.clone())
648 }
649
650 #[deprecated(
653 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."
654 )]
655 pub fn push_identity_resolver(
656 &mut self,
657 scheme_id: AuthSchemeId,
658 identity_resolver: impl ResolveIdentity + 'static,
659 ) -> &mut Self {
660 self.set_identity_resolver(scheme_id, identity_resolver)
661 }
662
663 pub fn set_identity_resolver(
668 &mut self,
669 scheme_id: AuthSchemeId,
670 identity_resolver: impl ResolveIdentity + 'static,
671 ) -> &mut Self {
672 let mut resolvers = self.identity_resolvers.take().unwrap_or_default();
673 resolvers.insert(
674 scheme_id,
675 Tracked::new(self.builder_name, identity_resolver.into_shared()),
676 );
677 self.identity_resolvers = Some(resolvers);
678 self
679 }
680
681 pub fn with_identity_resolver(
683 mut self,
684 scheme_id: AuthSchemeId,
685 identity_resolver: impl ResolveIdentity + 'static,
686 ) -> Self {
687 self.set_identity_resolver(scheme_id, identity_resolver);
688 self
689 }
690
691 pub fn interceptors(&self) -> impl Iterator<Item = SharedInterceptor> + '_ {
693 self.interceptors.iter().map(|s| s.value.clone())
694 }
695
696 pub fn extend_interceptors(
698 &mut self,
699 interceptors: impl Iterator<Item = SharedInterceptor>,
700 ) -> &mut Self {
701 self.interceptors
702 .extend(interceptors.map(|s| Tracked::new(self.builder_name, s)));
703 self
704 }
705
706 pub fn push_interceptor(&mut self, interceptor: impl Intercept + 'static) -> &mut Self {
708 self.interceptors
709 .push(Tracked::new(self.builder_name, interceptor.into_shared()));
710 self
711 }
712
713 pub fn with_interceptor(mut self, interceptor: impl Intercept + 'static) -> Self {
715 self.push_interceptor(interceptor);
716 self
717 }
718
719 pub fn set_interceptors(
721 &mut self,
722 interceptors: impl Iterator<Item = SharedInterceptor>,
723 ) -> &mut Self {
724 self.interceptors.clear();
725 self.interceptors
726 .extend(interceptors.map(|s| Tracked::new(self.builder_name, s)));
727 self
728 }
729
730 pub fn with_interceptors(
732 mut self,
733 interceptors: impl Iterator<Item = SharedInterceptor>,
734 ) -> Self {
735 self.set_interceptors(interceptors);
736 self
737 }
738
739 pub fn retry_classifiers(&self) -> impl Iterator<Item = SharedRetryClassifier> + '_ {
741 self.retry_classifiers.iter().map(|s| s.value.clone())
742 }
743
744 pub fn extend_retry_classifiers(
746 &mut self,
747 retry_classifiers: impl Iterator<Item = SharedRetryClassifier>,
748 ) -> &mut Self {
749 self.retry_classifiers
750 .extend(retry_classifiers.map(|s| Tracked::new(self.builder_name, s)));
751 self
752 }
753
754 pub fn push_retry_classifier(
756 &mut self,
757 retry_classifier: impl ClassifyRetry + 'static,
758 ) -> &mut Self {
759 self.retry_classifiers.push(Tracked::new(
760 self.builder_name,
761 retry_classifier.into_shared(),
762 ));
763 self
764 }
765
766 pub fn with_retry_classifier(mut self, retry_classifier: impl ClassifyRetry + 'static) -> Self {
768 self.push_retry_classifier(retry_classifier);
769 self
770 }
771
772 pub fn set_retry_classifiers(
774 &mut self,
775 retry_classifiers: impl Iterator<Item = SharedRetryClassifier>,
776 ) -> &mut Self {
777 self.retry_classifiers.clear();
778 self.retry_classifiers
779 .extend(retry_classifiers.map(|s| Tracked::new(self.builder_name, s)));
780 self
781 }
782
783 pub fn retry_strategy(&self) -> Option<SharedRetryStrategy> {
785 self.retry_strategy.as_ref().map(|s| s.value.clone())
786 }
787
788 pub fn set_retry_strategy(
790 &mut self,
791 retry_strategy: Option<impl RetryStrategy + 'static>,
792 ) -> &mut Self {
793 self.retry_strategy =
794 retry_strategy.map(|s| Tracked::new(self.builder_name, s.into_shared()));
795 self
796 }
797
798 pub fn with_retry_strategy(
800 mut self,
801 retry_strategy: Option<impl RetryStrategy + 'static>,
802 ) -> Self {
803 self.retry_strategy =
804 retry_strategy.map(|s| Tracked::new(self.builder_name, s.into_shared()));
805 self
806 }
807
808 pub fn sleep_impl(&self) -> Option<SharedAsyncSleep> {
810 self.sleep_impl.as_ref().map(|s| s.value.clone())
811 }
812
813 pub fn set_sleep_impl(&mut self, sleep_impl: Option<SharedAsyncSleep>) -> &mut Self {
815 self.sleep_impl = self.tracked(sleep_impl);
816 self
817 }
818
819 pub fn with_sleep_impl(mut self, sleep_impl: Option<impl AsyncSleep + 'static>) -> Self {
821 self.set_sleep_impl(sleep_impl.map(IntoShared::into_shared));
822 self
823 }
824
825 pub fn time_source(&self) -> Option<SharedTimeSource> {
827 self.time_source.as_ref().map(|s| s.value.clone())
828 }
829
830 pub fn set_time_source(&mut self, time_source: Option<SharedTimeSource>) -> &mut Self {
832 self.time_source = self.tracked(time_source);
833 self
834 }
835
836 pub fn with_time_source(mut self, time_source: Option<impl TimeSource + 'static>) -> Self {
838 self.set_time_source(time_source.map(IntoShared::into_shared));
839 self
840 }
841
842 pub fn config_validators(&self) -> impl Iterator<Item = SharedConfigValidator> + '_ {
844 self.config_validators.iter().map(|s| s.value.clone())
845 }
846
847 pub fn extend_config_validators(
849 &mut self,
850 config_validators: impl Iterator<Item = SharedConfigValidator>,
851 ) -> &mut Self {
852 self.config_validators
853 .extend(config_validators.map(|s| Tracked::new(self.builder_name, s)));
854 self
855 }
856
857 pub fn push_config_validator(
859 &mut self,
860 config_validator: impl ValidateConfig + 'static,
861 ) -> &mut Self {
862 self.config_validators.push(Tracked::new(
863 self.builder_name,
864 config_validator.into_shared(),
865 ));
866 self
867 }
868
869 pub fn with_config_validator(
871 mut self,
872 config_validator: impl ValidateConfig + 'static,
873 ) -> Self {
874 self.push_config_validator(config_validator);
875 self
876 }
877
878 pub fn validate_base_client_config(&self, cfg: &ConfigBag) -> Result<(), BoxError> {
882 macro_rules! validate {
883 ($field:expr) => {
884 #[allow(for_loops_over_fallibles)]
885 for entry in $field {
886 ValidateConfig::validate_base_client_config(&entry.value, self, cfg)?;
887 }
888 };
889 }
890
891 for validator in self.config_validators() {
892 validator.validate_base_client_config(self, cfg)?;
893 }
894 validate!(&self.http_client);
895 validate!(&self.endpoint_resolver);
896 if let Some(auth_schemes) = &self.auth_schemes {
897 validate!(auth_schemes.values())
898 }
899 validate!(&self.identity_cache);
900 if let Some(resolvers) = &self.identity_resolvers {
901 validate!(resolvers.values())
902 }
903 validate!(&self.interceptors);
904 validate!(&self.retry_strategy);
905 Ok(())
906 }
907
908 pub fn into_time_components(mut self) -> TimeComponents {
910 TimeComponents {
911 sleep_impl: self.sleep_impl.take().map(|s| s.value),
912 time_source: self.time_source.take().map(|s| s.value),
913 }
914 }
915
916 fn tracked<T>(&self, v: Option<T>) -> Option<Tracked<T>> {
918 v.map(|v| Tracked::new(self.builder_name, v))
919 }
920}
921
922#[derive(Debug)]
924pub struct TimeComponents {
925 sleep_impl: Option<SharedAsyncSleep>,
926 time_source: Option<SharedTimeSource>,
927}
928
929impl TimeComponents {
930 pub fn sleep_impl(&self) -> Option<SharedAsyncSleep> {
932 self.sleep_impl.clone()
933 }
934
935 pub fn time_source(&self) -> Option<SharedTimeSource> {
937 self.time_source.clone()
938 }
939}
940
941#[derive(Clone, Debug)]
942#[cfg_attr(test, derive(Eq, PartialEq))]
943pub(crate) struct Tracked<T> {
944 _origin: &'static str,
945 value: T,
946}
947
948impl<T> Tracked<T> {
949 fn new(origin: &'static str, value: T) -> Self {
950 Self {
951 _origin: origin,
952 value,
953 }
954 }
955
956 #[cfg(debug_assertions)]
957 pub(crate) fn value(&self) -> &T {
958 &self.value
959 }
960}
961
962impl RuntimeComponentsBuilder {
963 #[cfg(feature = "test-util")]
965 pub fn for_tests() -> Self {
966 use crate::client::endpoint::{EndpointFuture, EndpointResolverParams};
967 use crate::client::identity::IdentityFuture;
968
969 #[derive(Debug)]
970 struct FakeAuthSchemeOptionResolver;
971 impl ResolveAuthSchemeOptions for FakeAuthSchemeOptionResolver {
972 fn resolve_auth_scheme_options(
973 &self,
974 _: &crate::client::auth::AuthSchemeOptionResolverParams,
975 ) -> Result<std::borrow::Cow<'_, [AuthSchemeId]>, BoxError> {
976 unreachable!("fake auth scheme option resolver must be overridden for this test")
977 }
978 }
979
980 #[derive(Debug)]
981 struct FakeClient;
982 impl HttpClient for FakeClient {
983 fn http_connector(
984 &self,
985 _: &crate::client::http::HttpConnectorSettings,
986 _: &RuntimeComponents,
987 ) -> crate::client::http::SharedHttpConnector {
988 unreachable!("fake client must be overridden for this test")
989 }
990 }
991
992 #[derive(Debug)]
993 struct FakeEndpointResolver;
994 impl ResolveEndpoint for FakeEndpointResolver {
995 fn resolve_endpoint<'a>(&'a self, _: &'a EndpointResolverParams) -> EndpointFuture<'a> {
996 unreachable!("fake endpoint resolver must be overridden for this test")
997 }
998 }
999
1000 #[derive(Debug)]
1001 struct FakeAuthScheme;
1002 impl AuthScheme for FakeAuthScheme {
1003 fn scheme_id(&self) -> AuthSchemeId {
1004 AuthSchemeId::new("fake")
1005 }
1006
1007 fn identity_resolver(
1008 &self,
1009 _: &dyn GetIdentityResolver,
1010 ) -> Option<SharedIdentityResolver> {
1011 None
1012 }
1013
1014 fn signer(&self) -> &dyn crate::client::auth::Sign {
1015 unreachable!("fake http auth scheme must be overridden for this test")
1016 }
1017 }
1018
1019 #[derive(Debug)]
1020 struct FakeIdentityResolver;
1021 impl ResolveIdentity for FakeIdentityResolver {
1022 fn resolve_identity<'a>(
1023 &'a self,
1024 _: &'a RuntimeComponents,
1025 _: &'a ConfigBag,
1026 ) -> IdentityFuture<'a> {
1027 unreachable!("fake identity resolver must be overridden for this test")
1028 }
1029 }
1030
1031 #[derive(Debug)]
1032 struct FakeRetryStrategy;
1033 impl RetryStrategy for FakeRetryStrategy {
1034 fn should_attempt_initial_request(
1035 &self,
1036 _: &RuntimeComponents,
1037 _: &ConfigBag,
1038 ) -> Result<crate::client::retries::ShouldAttempt, BoxError> {
1039 unreachable!("fake retry strategy must be overridden for this test")
1040 }
1041
1042 fn should_attempt_retry(
1043 &self,
1044 _: &crate::client::interceptors::context::InterceptorContext,
1045 _: &RuntimeComponents,
1046 _: &ConfigBag,
1047 ) -> Result<crate::client::retries::ShouldAttempt, BoxError> {
1048 unreachable!("fake retry strategy must be overridden for this test")
1049 }
1050 }
1051
1052 #[derive(Debug)]
1053 struct FakeTimeSource;
1054 impl TimeSource for FakeTimeSource {
1055 fn now(&self) -> std::time::SystemTime {
1056 unreachable!("fake time source must be overridden for this test")
1057 }
1058 }
1059
1060 #[derive(Debug)]
1061 struct FakeSleep;
1062 impl AsyncSleep for FakeSleep {
1063 fn sleep(&self, _: std::time::Duration) -> aws_smithy_async::rt::sleep::Sleep {
1064 unreachable!("fake sleep must be overridden for this test")
1065 }
1066 }
1067
1068 #[derive(Debug)]
1069 struct FakeIdentityCache;
1070 impl ResolveCachedIdentity for FakeIdentityCache {
1071 fn resolve_cached_identity<'a>(
1072 &'a self,
1073 resolver: SharedIdentityResolver,
1074 components: &'a RuntimeComponents,
1075 config_bag: &'a ConfigBag,
1076 ) -> IdentityFuture<'a> {
1077 IdentityFuture::new(async move {
1078 resolver.resolve_identity(components, config_bag).await
1079 })
1080 }
1081 }
1082
1083 Self::new("aws_smithy_runtime_api::client::runtime_components::RuntimeComponentBuilder::for_tests")
1084 .with_auth_scheme(FakeAuthScheme)
1085 .with_auth_scheme_option_resolver(Some(FakeAuthSchemeOptionResolver))
1086 .with_endpoint_resolver(Some(FakeEndpointResolver))
1087 .with_http_client(Some(FakeClient))
1088 .with_identity_cache(Some(FakeIdentityCache))
1089 .with_identity_resolver(AuthSchemeId::new("fake"), FakeIdentityResolver)
1090 .with_retry_strategy(Some(FakeRetryStrategy))
1091 .with_sleep_impl(Some(SharedAsyncSleep::new(FakeSleep)))
1092 .with_time_source(Some(SharedTimeSource::new(FakeTimeSource)))
1093 }
1094}
1095
1096#[derive(Debug)]
1098pub struct BuildError(&'static str);
1099
1100impl std::error::Error for BuildError {}
1101
1102impl fmt::Display for BuildError {
1103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1104 write!(f, "{}", self.0)
1105 }
1106}
1107
1108pub trait GetIdentityResolver: Send + Sync {
1113 fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option<SharedIdentityResolver>;
1115}
1116
1117impl GetIdentityResolver for RuntimeComponents {
1118 fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option<SharedIdentityResolver> {
1119 self.identity_resolvers
1120 .get(&scheme_id)
1121 .map(|s| s.value.clone())
1122 }
1123}
1124
1125#[cfg(all(test, feature = "test-util"))]
1126mod tests {
1127 use super::{BuildError, RuntimeComponentsBuilder, Tracked};
1128 use crate::client::runtime_components::ValidateConfig;
1129
1130 #[derive(Clone, Debug, Eq, PartialEq)]
1131 struct TestComponent(String);
1132 impl ValidateConfig for TestComponent {}
1133 impl From<&'static str> for TestComponent {
1134 fn from(value: &'static str) -> Self {
1135 TestComponent(value.into())
1136 }
1137 }
1138
1139 #[test]
1140 #[allow(unreachable_pub)]
1141 #[allow(dead_code)]
1142 fn the_builders_should_merge() {
1143 declare_runtime_components! {
1144 fields for TestRc and TestRcBuilder {
1145 #[required]
1146 some_required_component: Option<TestComponent>,
1147
1148 some_optional_component: Option<TestComponent>,
1149
1150 #[atLeastOneRequired]
1151 some_required_vec: Vec<TestComponent>,
1152
1153 some_optional_vec: Vec<TestComponent>,
1154 }
1155 }
1156
1157 impl TestRc {
1158 fn sort(&mut self) {}
1159 }
1160
1161 let builder1 = TestRcBuilder {
1162 builder_name: "builder1",
1163 some_required_component: Some(Tracked::new("builder1", "override_me".into())),
1164 some_optional_component: Some(Tracked::new("builder1", "override_me optional".into())),
1165 some_required_vec: vec![Tracked::new("builder1", "first".into())],
1166 some_optional_vec: vec![Tracked::new("builder1", "first optional".into())],
1167 };
1168 let builder2 = TestRcBuilder {
1169 builder_name: "builder2",
1170 some_required_component: Some(Tracked::new("builder2", "override_me_too".into())),
1171 some_optional_component: Some(Tracked::new(
1172 "builder2",
1173 "override_me_too optional".into(),
1174 )),
1175 some_required_vec: vec![Tracked::new("builder2", "second".into())],
1176 some_optional_vec: vec![Tracked::new("builder2", "second optional".into())],
1177 };
1178 let builder3 = TestRcBuilder {
1179 builder_name: "builder3",
1180 some_required_component: Some(Tracked::new("builder3", "correct".into())),
1181 some_optional_component: Some(Tracked::new("builder3", "correct optional".into())),
1182 some_required_vec: vec![Tracked::new("builder3", "third".into())],
1183 some_optional_vec: vec![Tracked::new("builder3", "third optional".into())],
1184 };
1185 let rc = TestRcBuilder::new("root")
1186 .merge_from(&builder1)
1187 .merge_from(&builder2)
1188 .merge_from(&builder3)
1189 .build()
1190 .expect("success");
1191 assert_eq!(
1192 Tracked::new("builder3", TestComponent::from("correct")),
1193 rc.some_required_component
1194 );
1195 assert_eq!(
1196 Some(Tracked::new(
1197 "builder3",
1198 TestComponent::from("correct optional")
1199 )),
1200 rc.some_optional_component
1201 );
1202 assert_eq!(
1203 vec![
1204 Tracked::new("builder1", TestComponent::from("first")),
1205 Tracked::new("builder2", TestComponent::from("second")),
1206 Tracked::new("builder3", TestComponent::from("third"))
1207 ],
1208 rc.some_required_vec
1209 );
1210 assert_eq!(
1211 vec![
1212 Tracked::new("builder1", TestComponent::from("first optional")),
1213 Tracked::new("builder2", TestComponent::from("second optional")),
1214 Tracked::new("builder3", TestComponent::from("third optional"))
1215 ],
1216 rc.some_optional_vec
1217 );
1218 }
1219
1220 #[test]
1221 #[allow(unreachable_pub)]
1222 #[allow(dead_code)]
1223 #[should_panic(expected = "the `_some_component` runtime component is required")]
1224 fn require_field_singular() {
1225 declare_runtime_components! {
1226 fields for TestRc and TestRcBuilder {
1227 #[required]
1228 _some_component: Option<TestComponent>,
1229 }
1230 }
1231
1232 impl TestRc {
1233 fn sort(&mut self) {}
1234 }
1235
1236 let rc = TestRcBuilder::new("test").build().unwrap();
1237
1238 let _: Tracked<TestComponent> = rc._some_component;
1240 }
1241
1242 #[test]
1243 #[allow(unreachable_pub)]
1244 #[allow(dead_code)]
1245 #[should_panic(expected = "at least one `_some_vec` runtime component is required")]
1246 fn require_field_plural() {
1247 declare_runtime_components! {
1248 fields for TestRc and TestRcBuilder {
1249 #[atLeastOneRequired]
1250 _some_vec: Vec<TestComponent>,
1251 }
1252 }
1253
1254 impl TestRc {
1255 fn sort(&mut self) {}
1256 }
1257
1258 let rc = TestRcBuilder::new("test").build().unwrap();
1259
1260 let _: Vec<Tracked<TestComponent>> = rc._some_vec;
1262 }
1263
1264 #[test]
1265 #[allow(unreachable_pub)]
1266 #[allow(dead_code)]
1267 fn optional_fields_dont_panic() {
1268 declare_runtime_components! {
1269 fields for TestRc and TestRcBuilder {
1270 _some_optional_component: Option<TestComponent>,
1271 _some_optional_vec: Vec<TestComponent>,
1272 }
1273 }
1274
1275 impl TestRc {
1276 fn sort(&mut self) {}
1277 }
1278
1279 let rc = TestRcBuilder::new("test").build().unwrap();
1280
1281 let _: Option<Tracked<TestComponent>> = rc._some_optional_component;
1283 let _: Vec<Tracked<TestComponent>> = rc._some_optional_vec;
1284 }
1285
1286 #[test]
1287 fn building_test_builder_should_not_panic() {
1288 let _ = RuntimeComponentsBuilder::for_tests().build(); }
1290
1291 #[test]
1292 fn set_identity_resolver_should_replace_existing_resolver_for_given_auth_scheme() {
1293 use crate::client::auth::AuthSchemeId;
1294 use crate::client::identity::{Identity, IdentityFuture, ResolveIdentity};
1295 use crate::client::runtime_components::{GetIdentityResolver, RuntimeComponents};
1296 use aws_smithy_types::config_bag::ConfigBag;
1297 use tokio::runtime::Runtime;
1298
1299 #[derive(Debug)]
1300 struct AnotherFakeIdentityResolver;
1301 impl ResolveIdentity for AnotherFakeIdentityResolver {
1302 fn resolve_identity<'a>(
1303 &'a self,
1304 _: &'a RuntimeComponents,
1305 _: &'a ConfigBag,
1306 ) -> IdentityFuture<'a> {
1307 IdentityFuture::ready(Ok(Identity::new("doesn't matter", None)))
1308 }
1309 }
1310
1311 let rc = RuntimeComponentsBuilder::for_tests()
1314 .with_identity_resolver(AuthSchemeId::new("fake"), AnotherFakeIdentityResolver)
1315 .build()
1316 .expect("should build RuntimeComponents");
1317
1318 let resolver = rc
1319 .identity_resolver(AuthSchemeId::new("fake"))
1320 .expect("identity resolver should be found");
1321
1322 let identity = Runtime::new().unwrap().block_on(async {
1323 resolver
1324 .resolve_identity(&rc, &ConfigBag::base())
1325 .await
1326 .expect("identity should be resolved")
1327 });
1328
1329 assert_eq!(Some(&"doesn't matter"), identity.data::<&str>());
1330 }
1331}