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::http::HttpClient;
16use aws_smithy_runtime_api::shared::IntoShared;
17use aws_smithy_types::error::display::DisplayErrorContext;
18use aws_smithy_types::retry::RetryConfig;
19use aws_types::os_shim_internal::{Env, Fs};
20use aws_types::region::Region;
21use aws_types::sdk_config::SharedHttpClient;
22use aws_types::SdkConfig;
23use std::borrow::Cow;
24use std::fmt::{Debug, Formatter};
25use std::sync::Arc;
26use tokio::sync::OnceCell;
27
28#[derive(Clone)]
37pub struct ProviderConfig {
38 env: Env,
39 fs: Fs,
40 time_source: SharedTimeSource,
41 http_client: Option<SharedHttpClient>,
42 retry_config: Option<RetryConfig>,
43 sleep_impl: Option<SharedAsyncSleep>,
44 region: Option<Region>,
45 use_fips: Option<bool>,
46 use_dual_stack: Option<bool>,
47 parsed_profile: Arc<OnceCell<Result<ProfileSet, ProfileFileLoadError>>>,
49 #[allow(deprecated)]
51 profile_files: ProfileFiles,
52 profile_name_override: Option<Cow<'static, str>>,
54}
55
56impl Debug for ProviderConfig {
57 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
58 f.debug_struct("ProviderConfig")
59 .field("env", &self.env)
60 .field("fs", &self.fs)
61 .field("time_source", &self.time_source)
62 .field("http_client", &self.http_client)
63 .field("retry_config", &self.retry_config)
64 .field("sleep_impl", &self.sleep_impl)
65 .field("region", &self.region)
66 .field("use_fips", &self.use_fips)
67 .field("use_dual_stack", &self.use_dual_stack)
68 .field("profile_name_override", &self.profile_name_override)
69 .finish()
70 }
71}
72
73impl Default for ProviderConfig {
74 fn default() -> Self {
75 Self {
76 env: Env::default(),
77 fs: Fs::default(),
78 time_source: SharedTimeSource::default(),
79 http_client: None,
80 retry_config: None,
81 sleep_impl: default_async_sleep(),
82 region: None,
83 use_fips: None,
84 use_dual_stack: None,
85 parsed_profile: Default::default(),
86 #[allow(deprecated)]
87 profile_files: ProfileFiles::default(),
88 profile_name_override: None,
89 }
90 }
91}
92
93#[cfg(test)]
94impl ProviderConfig {
95 pub fn no_configuration() -> Self {
100 use aws_smithy_async::time::StaticTimeSource;
101 use std::collections::HashMap;
102 use std::time::UNIX_EPOCH;
103 let fs = Fs::from_raw_map(HashMap::new());
104 let env = Env::from_slice(&[]);
105 Self {
106 parsed_profile: Default::default(),
107 #[allow(deprecated)]
108 profile_files: ProfileFiles::default(),
109 env,
110 fs,
111 time_source: SharedTimeSource::new(StaticTimeSource::new(UNIX_EPOCH)),
112 http_client: None,
113 retry_config: None,
114 sleep_impl: None,
115 region: None,
116 use_fips: None,
117 use_dual_stack: None,
118 profile_name_override: None,
119 }
120 }
121}
122
123impl ProviderConfig {
124 pub fn without_region() -> Self {
146 Self::default()
147 }
148
149 pub fn empty() -> Self {
151 ProviderConfig {
152 env: Env::default(),
153 fs: Fs::default(),
154 time_source: SharedTimeSource::default(),
155 http_client: None,
156 retry_config: None,
157 sleep_impl: None,
158 region: None,
159 use_fips: None,
160 use_dual_stack: None,
161 parsed_profile: Default::default(),
162 #[allow(deprecated)]
163 profile_files: ProfileFiles::default(),
164 profile_name_override: None,
165 }
166 }
167
168 pub(crate) fn init(
170 time_source: SharedTimeSource,
171 sleep_impl: Option<SharedAsyncSleep>,
172 ) -> Self {
173 Self {
174 parsed_profile: Default::default(),
175 #[allow(deprecated)]
176 profile_files: ProfileFiles::default(),
177 env: Env::default(),
178 fs: Fs::default(),
179 time_source,
180 http_client: None,
181 retry_config: None,
182 sleep_impl,
183 region: None,
184 use_fips: None,
185 use_dual_stack: None,
186 profile_name_override: None,
187 }
188 }
189
190 pub async fn with_default_region() -> Self {
203 Self::without_region().load_default_region().await
204 }
205
206 pub(crate) fn client_config(&self) -> SdkConfig {
213 let profiles = self.parsed_profile.get().and_then(|v| v.as_ref().ok());
214 let service_config = EnvServiceConfig {
215 env: self.env(),
216 env_config_sections: profiles.cloned().unwrap_or_default(),
217 };
218
219 let mut builder = SdkConfig::builder()
220 .retry_config(
221 self.retry_config
222 .as_ref()
223 .map_or(RetryConfig::standard(), |config| config.clone()),
224 )
225 .region(self.region())
226 .time_source(self.time_source())
227 .use_fips(self.use_fips().unwrap_or_default())
228 .use_dual_stack(self.use_dual_stack().unwrap_or_default())
229 .service_config(service_config)
230 .behavior_version(crate::BehaviorVersion::latest());
231 builder.set_http_client(self.http_client.clone());
232 builder.set_sleep_impl(self.sleep_impl.clone());
233 builder.build()
234 }
235
236 #[allow(dead_code)]
239 pub(crate) fn env(&self) -> Env {
240 self.env.clone()
241 }
242
243 #[allow(dead_code)]
244 pub(crate) fn fs(&self) -> Fs {
245 self.fs.clone()
246 }
247
248 #[allow(dead_code)]
249 pub(crate) fn time_source(&self) -> SharedTimeSource {
250 self.time_source.clone()
251 }
252
253 #[allow(dead_code)]
254 pub(crate) fn http_client(&self) -> Option<SharedHttpClient> {
255 self.http_client.clone()
256 }
257
258 #[allow(dead_code)]
259 pub(crate) fn retry_config(&self) -> Option<RetryConfig> {
260 self.retry_config.clone()
261 }
262
263 #[allow(dead_code)]
264 pub(crate) fn sleep_impl(&self) -> Option<SharedAsyncSleep> {
265 self.sleep_impl.clone()
266 }
267
268 #[allow(dead_code)]
269 pub(crate) fn region(&self) -> Option<Region> {
270 self.region.clone()
271 }
272
273 #[allow(dead_code)]
274 pub(crate) fn use_fips(&self) -> Option<bool> {
275 self.use_fips
276 }
277
278 #[allow(dead_code)]
279 pub(crate) fn use_dual_stack(&self) -> Option<bool> {
280 self.use_dual_stack
281 }
282
283 pub(crate) async fn try_profile(&self) -> Result<&ProfileSet, &ProfileFileLoadError> {
284 let parsed_profile = self
285 .parsed_profile
286 .get_or_init(|| async {
287 let profile = profile::load(
288 &self.fs,
289 &self.env,
290 &self.profile_files,
291 self.profile_name_override.clone(),
292 )
293 .await;
294 if let Err(err) = profile.as_ref() {
295 tracing::warn!(err = %DisplayErrorContext(&err), "failed to parse profile")
296 }
297 profile
298 })
299 .await;
300 parsed_profile.as_ref()
301 }
302
303 pub(crate) async fn profile(&self) -> Option<&ProfileSet> {
304 self.try_profile().await.ok()
305 }
306
307 pub fn with_region(mut self, region: Option<Region>) -> Self {
309 self.region = region;
310 self
311 }
312
313 pub(crate) fn with_use_fips(mut self, use_fips: Option<bool>) -> Self {
315 self.use_fips = use_fips;
316 self
317 }
318
319 pub(crate) fn with_use_dual_stack(mut self, use_dual_stack: Option<bool>) -> Self {
321 self.use_dual_stack = use_dual_stack;
322 self
323 }
324
325 pub(crate) fn with_profile_name(self, profile_name: String) -> Self {
326 let profile_files = self.profile_files.clone();
327 self.with_profile_config(Some(profile_files), Some(profile_name))
328 }
329
330 #[allow(deprecated)]
332 pub(crate) fn with_profile_config(
333 self,
334 profile_files: Option<ProfileFiles>,
335 profile_name_override: Option<String>,
336 ) -> Self {
337 if profile_files.is_none() && profile_name_override.is_none() {
339 return self;
340 }
341 ProviderConfig {
342 parsed_profile: Default::default(),
344 profile_files: profile_files.unwrap_or(self.profile_files),
345 profile_name_override: profile_name_override
346 .map(Cow::Owned)
347 .or(self.profile_name_override),
348 ..self
349 }
350 }
351
352 pub async fn load_default_region(self) -> Self {
357 use crate::default_provider::region::DefaultRegionChain;
358 let provider_chain = DefaultRegionChain::builder().configure(&self).build();
359 self.with_region(provider_chain.region().await)
360 }
361
362 pub(crate) fn with_fs(self, fs: Fs) -> Self {
363 ProviderConfig {
364 parsed_profile: Default::default(),
365 fs,
366 ..self
367 }
368 }
369
370 pub(crate) fn with_env(self, env: Env) -> Self {
371 ProviderConfig {
372 parsed_profile: Default::default(),
373 env,
374 ..self
375 }
376 }
377
378 pub fn with_time_source(self, time_source: impl TimeSource + 'static) -> Self {
380 ProviderConfig {
381 time_source: time_source.into_shared(),
382 ..self
383 }
384 }
385
386 pub fn with_http_client(self, http_client: impl HttpClient + 'static) -> Self {
388 ProviderConfig {
389 http_client: Some(http_client.into_shared()),
390 ..self
391 }
392 }
393
394 pub fn with_sleep_impl(self, sleep_impl: impl AsyncSleep + 'static) -> Self {
396 ProviderConfig {
397 sleep_impl: Some(sleep_impl.into_shared()),
398 ..self
399 }
400 }
401
402 pub fn with_retry_config(self, retry_config: RetryConfig) -> Self {
404 ProviderConfig {
405 retry_config: Some(retry_config),
406 ..self
407 }
408 }
409}