1use crate::env_service_config::EnvServiceConfig;
9use crate::profile;
10#[allow(deprecated)]
11use crate::profile::profile_file::ProfileFiles;
12use crate::profile::{ProfileFileLoadError, ProfileSet};
13use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep};
14use aws_smithy_async::time::{SharedTimeSource, TimeSource};
15use aws_smithy_runtime_api::client::behavior_version::BehaviorVersion;
16use aws_smithy_runtime_api::client::http::HttpClient;
17use aws_smithy_runtime_api::shared::IntoShared;
18use aws_smithy_types::error::display::DisplayErrorContext;
19use aws_smithy_types::retry::RetryConfig;
20use aws_types::os_shim_internal::{Env, Fs};
21use aws_types::region::Region;
22use aws_types::sdk_config::SharedHttpClient;
23use aws_types::SdkConfig;
24use std::borrow::Cow;
25use std::fmt::{Debug, Formatter};
26use std::sync::Arc;
27use tokio::sync::OnceCell;
28
29#[derive(Clone)]
38pub struct ProviderConfig {
39 env: Env,
40 fs: Fs,
41 time_source: SharedTimeSource,
42 http_client: Option<SharedHttpClient>,
43 retry_config: Option<RetryConfig>,
44 sleep_impl: Option<SharedAsyncSleep>,
45 region: Option<Region>,
46 use_fips: Option<bool>,
47 use_dual_stack: Option<bool>,
48 behavior_version: Option<BehaviorVersion>,
49 parsed_profile: Arc<OnceCell<Result<ProfileSet, ProfileFileLoadError>>>,
51 #[allow(deprecated)]
53 profile_files: ProfileFiles,
54 profile_name_override: Option<Cow<'static, str>>,
56}
57
58impl Debug for ProviderConfig {
59 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
60 f.debug_struct("ProviderConfig")
61 .field("env", &self.env)
62 .field("fs", &self.fs)
63 .field("time_source", &self.time_source)
64 .field("http_client", &self.http_client)
65 .field("retry_config", &self.retry_config)
66 .field("sleep_impl", &self.sleep_impl)
67 .field("region", &self.region)
68 .field("use_fips", &self.use_fips)
69 .field("use_dual_stack", &self.use_dual_stack)
70 .field("profile_name_override", &self.profile_name_override)
71 .finish()
72 }
73}
74
75impl Default for ProviderConfig {
76 fn default() -> Self {
77 Self {
78 env: Env::default(),
79 fs: Fs::default(),
80 time_source: SharedTimeSource::default(),
81 http_client: None,
82 retry_config: None,
83 sleep_impl: default_async_sleep(),
84 region: None,
85 use_fips: None,
86 use_dual_stack: None,
87 behavior_version: None,
88 parsed_profile: Default::default(),
89 #[allow(deprecated)]
90 profile_files: ProfileFiles::default(),
91 profile_name_override: None,
92 }
93 }
94}
95
96#[cfg(test)]
97impl ProviderConfig {
98 pub fn no_configuration() -> Self {
103 use aws_smithy_async::time::StaticTimeSource;
104 use std::collections::HashMap;
105 use std::time::UNIX_EPOCH;
106 let fs = Fs::from_raw_map(HashMap::new());
107 let env = Env::from_slice(&[]);
108 Self {
109 parsed_profile: Default::default(),
110 #[allow(deprecated)]
111 profile_files: ProfileFiles::default(),
112 env,
113 fs,
114 time_source: SharedTimeSource::new(StaticTimeSource::new(UNIX_EPOCH)),
115 http_client: None,
116 retry_config: None,
117 sleep_impl: None,
118 region: None,
119 use_fips: None,
120 use_dual_stack: None,
121 behavior_version: None,
122 profile_name_override: None,
123 }
124 }
125}
126
127impl ProviderConfig {
128 pub fn without_region() -> Self {
150 Self::default()
151 }
152
153 pub fn empty() -> Self {
155 ProviderConfig {
156 env: Env::default(),
157 fs: Fs::default(),
158 time_source: SharedTimeSource::default(),
159 http_client: None,
160 retry_config: None,
161 sleep_impl: None,
162 region: None,
163 use_fips: None,
164 use_dual_stack: None,
165 behavior_version: None,
166 parsed_profile: Default::default(),
167 #[allow(deprecated)]
168 profile_files: ProfileFiles::default(),
169 profile_name_override: None,
170 }
171 }
172
173 pub(crate) fn init(
175 time_source: SharedTimeSource,
176 sleep_impl: Option<SharedAsyncSleep>,
177 ) -> Self {
178 Self {
179 parsed_profile: Default::default(),
180 #[allow(deprecated)]
181 profile_files: ProfileFiles::default(),
182 env: Env::default(),
183 fs: Fs::default(),
184 time_source,
185 http_client: None,
186 retry_config: None,
187 sleep_impl,
188 region: None,
189 use_fips: None,
190 use_dual_stack: None,
191 behavior_version: None,
192 profile_name_override: None,
193 }
194 }
195
196 pub async fn with_default_region() -> Self {
209 Self::without_region().load_default_region().await
210 }
211
212 pub(crate) fn client_config(&self) -> SdkConfig {
219 let profiles = self.parsed_profile.get().and_then(|v| v.as_ref().ok());
220 let service_config = EnvServiceConfig {
221 env: self.env(),
222 env_config_sections: profiles.cloned().unwrap_or_default(),
223 ignore_configured_endpoint_urls: false,
224 };
225
226 let mut builder = SdkConfig::builder()
227 .retry_config(
228 self.retry_config
229 .as_ref()
230 .map_or(RetryConfig::standard(), |config| config.clone()),
231 )
232 .region(self.region())
233 .time_source(self.time_source())
234 .use_fips(self.use_fips().unwrap_or_default())
235 .use_dual_stack(self.use_dual_stack().unwrap_or_default())
236 .service_config(service_config)
237 .behavior_version(crate::BehaviorVersion::latest());
238 builder.set_http_client(self.http_client.clone());
239 builder.set_sleep_impl(self.sleep_impl.clone());
240 builder.build()
241 }
242
243 #[allow(dead_code)]
246 pub(crate) fn env(&self) -> Env {
247 self.env.clone()
248 }
249
250 #[allow(dead_code)]
251 pub(crate) fn fs(&self) -> Fs {
252 self.fs.clone()
253 }
254
255 #[allow(dead_code)]
256 pub(crate) fn time_source(&self) -> SharedTimeSource {
257 self.time_source.clone()
258 }
259
260 #[allow(dead_code)]
261 pub(crate) fn http_client(&self) -> Option<SharedHttpClient> {
262 self.http_client.clone()
263 }
264
265 #[allow(dead_code)]
266 pub(crate) fn retry_config(&self) -> Option<RetryConfig> {
267 self.retry_config.clone()
268 }
269
270 #[allow(dead_code)]
271 pub(crate) fn sleep_impl(&self) -> Option<SharedAsyncSleep> {
272 self.sleep_impl.clone()
273 }
274
275 #[allow(dead_code)]
276 pub(crate) fn region(&self) -> Option<Region> {
277 self.region.clone()
278 }
279
280 #[allow(dead_code)]
281 pub(crate) fn use_fips(&self) -> Option<bool> {
282 self.use_fips
283 }
284
285 #[allow(dead_code)]
286 pub(crate) fn use_dual_stack(&self) -> Option<bool> {
287 self.use_dual_stack
288 }
289
290 pub(crate) async fn try_profile(&self) -> Result<&ProfileSet, &ProfileFileLoadError> {
291 let parsed_profile = self
292 .parsed_profile
293 .get_or_init(|| async {
294 let profile = profile::load(
295 &self.fs,
296 &self.env,
297 &self.profile_files,
298 self.profile_name_override.clone(),
299 )
300 .await;
301 if let Err(err) = profile.as_ref() {
302 tracing::warn!(err = %DisplayErrorContext(&err), "failed to parse profile")
303 }
304 profile
305 })
306 .await;
307 parsed_profile.as_ref()
308 }
309
310 pub(crate) async fn profile(&self) -> Option<&ProfileSet> {
311 self.try_profile().await.ok()
312 }
313
314 pub fn with_region(mut self, region: Option<Region>) -> Self {
316 self.region = region;
317 self
318 }
319
320 pub fn with_use_fips(mut self, use_fips: Option<bool>) -> Self {
333 self.use_fips = use_fips;
334 self
335 }
336
337 pub fn with_use_dual_stack(mut self, use_dual_stack: Option<bool>) -> Self {
345 self.use_dual_stack = use_dual_stack;
346 self
347 }
348
349 pub(crate) fn behavior_version(&self) -> Option<BehaviorVersion> {
350 self.behavior_version
351 }
352
353 pub fn with_behavior_version(mut self, behavior_version: Option<BehaviorVersion>) -> Self {
355 self.behavior_version = behavior_version;
356 self
357 }
358
359 pub(crate) fn with_profile_name(self, profile_name: String) -> Self {
360 let profile_files = self.profile_files.clone();
361 self.with_profile_config(Some(profile_files), Some(profile_name))
362 }
363
364 #[allow(deprecated)]
366 pub(crate) fn with_profile_config(
367 self,
368 profile_files: Option<ProfileFiles>,
369 profile_name_override: Option<String>,
370 ) -> Self {
371 if profile_files.is_none() && profile_name_override.is_none() {
373 return self;
374 }
375 ProviderConfig {
376 parsed_profile: Default::default(),
378 profile_files: profile_files.unwrap_or(self.profile_files),
379 profile_name_override: profile_name_override
380 .map(Cow::Owned)
381 .or(self.profile_name_override),
382 ..self
383 }
384 }
385
386 pub async fn load_default_region(self) -> Self {
391 use crate::default_provider::region::DefaultRegionChain;
392 let provider_chain = DefaultRegionChain::builder().configure(&self).build();
393 self.with_region(provider_chain.region().await)
394 }
395
396 pub(crate) fn with_fs(self, fs: Fs) -> Self {
397 ProviderConfig {
398 parsed_profile: Default::default(),
399 fs,
400 ..self
401 }
402 }
403
404 pub(crate) fn with_env(self, env: Env) -> Self {
405 ProviderConfig {
406 parsed_profile: Default::default(),
407 env,
408 ..self
409 }
410 }
411
412 pub fn with_time_source(self, time_source: impl TimeSource + 'static) -> Self {
414 ProviderConfig {
415 time_source: time_source.into_shared(),
416 ..self
417 }
418 }
419
420 pub fn with_http_client(self, http_client: impl HttpClient + 'static) -> Self {
422 ProviderConfig {
423 http_client: Some(http_client.into_shared()),
424 ..self
425 }
426 }
427
428 pub fn with_sleep_impl(self, sleep_impl: impl AsyncSleep + 'static) -> Self {
430 ProviderConfig {
431 sleep_impl: Some(sleep_impl.into_shared()),
432 ..self
433 }
434 }
435
436 pub fn with_retry_config(self, retry_config: RetryConfig) -> Self {
438 ProviderConfig {
439 retry_config: Some(retry_config),
440 ..self
441 }
442 }
443}