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 sleep_impl: Option<SharedAsyncSleep>,
43 region: Option<Region>,
44 use_fips: Option<bool>,
45 use_dual_stack: Option<bool>,
46 parsed_profile: Arc<OnceCell<Result<ProfileSet, ProfileFileLoadError>>>,
48 #[allow(deprecated)]
50 profile_files: ProfileFiles,
51 profile_name_override: Option<Cow<'static, str>>,
53}
54
55impl Debug for ProviderConfig {
56 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
57 f.debug_struct("ProviderConfig")
58 .field("env", &self.env)
59 .field("fs", &self.fs)
60 .field("time_source", &self.time_source)
61 .field("http_client", &self.http_client)
62 .field("sleep_impl", &self.sleep_impl)
63 .field("region", &self.region)
64 .field("use_fips", &self.use_fips)
65 .field("use_dual_stack", &self.use_dual_stack)
66 .field("profile_name_override", &self.profile_name_override)
67 .finish()
68 }
69}
70
71impl Default for ProviderConfig {
72 fn default() -> Self {
73 Self {
74 env: Env::default(),
75 fs: Fs::default(),
76 time_source: SharedTimeSource::default(),
77 http_client: None,
78 sleep_impl: default_async_sleep(),
79 region: None,
80 use_fips: None,
81 use_dual_stack: None,
82 parsed_profile: Default::default(),
83 #[allow(deprecated)]
84 profile_files: ProfileFiles::default(),
85 profile_name_override: None,
86 }
87 }
88}
89
90#[cfg(test)]
91impl ProviderConfig {
92 pub fn no_configuration() -> Self {
97 use aws_smithy_async::time::StaticTimeSource;
98 use std::collections::HashMap;
99 use std::time::UNIX_EPOCH;
100 let fs = Fs::from_raw_map(HashMap::new());
101 let env = Env::from_slice(&[]);
102 Self {
103 parsed_profile: Default::default(),
104 #[allow(deprecated)]
105 profile_files: ProfileFiles::default(),
106 env,
107 fs,
108 time_source: SharedTimeSource::new(StaticTimeSource::new(UNIX_EPOCH)),
109 http_client: None,
110 sleep_impl: None,
111 region: None,
112 use_fips: None,
113 use_dual_stack: None,
114 profile_name_override: None,
115 }
116 }
117}
118
119impl ProviderConfig {
120 pub fn without_region() -> Self {
142 Self::default()
143 }
144
145 pub fn empty() -> Self {
147 ProviderConfig {
148 env: Env::default(),
149 fs: Fs::default(),
150 time_source: SharedTimeSource::default(),
151 http_client: None,
152 sleep_impl: None,
153 region: None,
154 use_fips: None,
155 use_dual_stack: None,
156 parsed_profile: Default::default(),
157 #[allow(deprecated)]
158 profile_files: ProfileFiles::default(),
159 profile_name_override: None,
160 }
161 }
162
163 pub(crate) fn init(
165 time_source: SharedTimeSource,
166 sleep_impl: Option<SharedAsyncSleep>,
167 ) -> Self {
168 Self {
169 parsed_profile: Default::default(),
170 #[allow(deprecated)]
171 profile_files: ProfileFiles::default(),
172 env: Env::default(),
173 fs: Fs::default(),
174 time_source,
175 http_client: None,
176 sleep_impl,
177 region: None,
178 use_fips: None,
179 use_dual_stack: None,
180 profile_name_override: None,
181 }
182 }
183
184 pub async fn with_default_region() -> Self {
197 Self::without_region().load_default_region().await
198 }
199
200 pub(crate) fn client_config(&self) -> SdkConfig {
207 let profiles = self.parsed_profile.get().and_then(|v| v.as_ref().ok());
208 let service_config = EnvServiceConfig {
209 env: self.env(),
210 env_config_sections: profiles.cloned().unwrap_or_default(),
211 };
212
213 let mut builder = SdkConfig::builder()
214 .retry_config(RetryConfig::standard())
215 .region(self.region())
216 .time_source(self.time_source())
217 .use_fips(self.use_fips().unwrap_or_default())
218 .use_dual_stack(self.use_dual_stack().unwrap_or_default())
219 .service_config(service_config)
220 .behavior_version(crate::BehaviorVersion::latest());
221 builder.set_http_client(self.http_client.clone());
222 builder.set_sleep_impl(self.sleep_impl.clone());
223 builder.build()
224 }
225
226 #[allow(dead_code)]
229 pub(crate) fn env(&self) -> Env {
230 self.env.clone()
231 }
232
233 #[allow(dead_code)]
234 pub(crate) fn fs(&self) -> Fs {
235 self.fs.clone()
236 }
237
238 #[allow(dead_code)]
239 pub(crate) fn time_source(&self) -> SharedTimeSource {
240 self.time_source.clone()
241 }
242
243 #[allow(dead_code)]
244 pub(crate) fn http_client(&self) -> Option<SharedHttpClient> {
245 self.http_client.clone()
246 }
247
248 #[allow(dead_code)]
249 pub(crate) fn sleep_impl(&self) -> Option<SharedAsyncSleep> {
250 self.sleep_impl.clone()
251 }
252
253 #[allow(dead_code)]
254 pub(crate) fn region(&self) -> Option<Region> {
255 self.region.clone()
256 }
257
258 #[allow(dead_code)]
259 pub(crate) fn use_fips(&self) -> Option<bool> {
260 self.use_fips
261 }
262
263 #[allow(dead_code)]
264 pub(crate) fn use_dual_stack(&self) -> Option<bool> {
265 self.use_dual_stack
266 }
267
268 pub(crate) async fn try_profile(&self) -> Result<&ProfileSet, &ProfileFileLoadError> {
269 let parsed_profile = self
270 .parsed_profile
271 .get_or_init(|| async {
272 let profile = profile::load(
273 &self.fs,
274 &self.env,
275 &self.profile_files,
276 self.profile_name_override.clone(),
277 )
278 .await;
279 if let Err(err) = profile.as_ref() {
280 tracing::warn!(err = %DisplayErrorContext(&err), "failed to parse profile")
281 }
282 profile
283 })
284 .await;
285 parsed_profile.as_ref()
286 }
287
288 pub(crate) async fn profile(&self) -> Option<&ProfileSet> {
289 self.try_profile().await.ok()
290 }
291
292 pub fn with_region(mut self, region: Option<Region>) -> Self {
294 self.region = region;
295 self
296 }
297
298 pub(crate) fn with_use_fips(mut self, use_fips: Option<bool>) -> Self {
300 self.use_fips = use_fips;
301 self
302 }
303
304 pub(crate) fn with_use_dual_stack(mut self, use_dual_stack: Option<bool>) -> Self {
306 self.use_dual_stack = use_dual_stack;
307 self
308 }
309
310 pub(crate) fn with_profile_name(self, profile_name: String) -> Self {
311 let profile_files = self.profile_files.clone();
312 self.with_profile_config(Some(profile_files), Some(profile_name))
313 }
314
315 #[allow(deprecated)]
317 pub(crate) fn with_profile_config(
318 self,
319 profile_files: Option<ProfileFiles>,
320 profile_name_override: Option<String>,
321 ) -> Self {
322 if profile_files.is_none() && profile_name_override.is_none() {
324 return self;
325 }
326 ProviderConfig {
327 parsed_profile: Default::default(),
329 profile_files: profile_files.unwrap_or(self.profile_files),
330 profile_name_override: profile_name_override
331 .map(Cow::Owned)
332 .or(self.profile_name_override),
333 ..self
334 }
335 }
336
337 pub async fn load_default_region(self) -> Self {
342 use crate::default_provider::region::DefaultRegionChain;
343 let provider_chain = DefaultRegionChain::builder().configure(&self).build();
344 self.with_region(provider_chain.region().await)
345 }
346
347 pub(crate) fn with_fs(self, fs: Fs) -> Self {
348 ProviderConfig {
349 parsed_profile: Default::default(),
350 fs,
351 ..self
352 }
353 }
354
355 pub(crate) fn with_env(self, env: Env) -> Self {
356 ProviderConfig {
357 parsed_profile: Default::default(),
358 env,
359 ..self
360 }
361 }
362
363 pub fn with_time_source(self, time_source: impl TimeSource + 'static) -> Self {
365 ProviderConfig {
366 time_source: time_source.into_shared(),
367 ..self
368 }
369 }
370
371 pub fn with_http_client(self, http_client: impl HttpClient + 'static) -> Self {
373 ProviderConfig {
374 http_client: Some(http_client.into_shared()),
375 ..self
376 }
377 }
378
379 pub fn with_sleep_impl(self, sleep_impl: impl AsyncSleep + 'static) -> Self {
381 ProviderConfig {
382 sleep_impl: Some(sleep_impl.into_shared()),
383 ..self
384 }
385 }
386}