1use crate::profile;
9#[allow(deprecated)]
10use crate::profile::profile_file::ProfileFiles;
11use crate::profile::{ProfileFileLoadError, ProfileSet};
12use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep};
13use aws_smithy_async::time::{SharedTimeSource, TimeSource};
14use aws_smithy_runtime_api::client::http::HttpClient;
15use aws_smithy_runtime_api::shared::IntoShared;
16use aws_smithy_types::error::display::DisplayErrorContext;
17use aws_smithy_types::retry::RetryConfig;
18use aws_types::os_shim_internal::{Env, Fs};
19use aws_types::region::Region;
20use aws_types::sdk_config::SharedHttpClient;
21use aws_types::SdkConfig;
22use std::borrow::Cow;
23use std::fmt::{Debug, Formatter};
24use std::sync::Arc;
25use tokio::sync::OnceCell;
26
27#[derive(Clone)]
36pub struct ProviderConfig {
37 env: Env,
38 fs: Fs,
39 time_source: SharedTimeSource,
40 http_client: Option<SharedHttpClient>,
41 sleep_impl: Option<SharedAsyncSleep>,
42 region: Option<Region>,
43 use_fips: Option<bool>,
44 use_dual_stack: Option<bool>,
45 parsed_profile: Arc<OnceCell<Result<ProfileSet, ProfileFileLoadError>>>,
47 #[allow(deprecated)]
49 profile_files: ProfileFiles,
50 profile_name_override: Option<Cow<'static, str>>,
52}
53
54impl Debug for ProviderConfig {
55 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
56 f.debug_struct("ProviderConfig")
57 .field("env", &self.env)
58 .field("fs", &self.fs)
59 .field("time_source", &self.time_source)
60 .field("http_client", &self.http_client)
61 .field("sleep_impl", &self.sleep_impl)
62 .field("region", &self.region)
63 .field("use_fips", &self.use_fips)
64 .field("use_dual_stack", &self.use_dual_stack)
65 .field("profile_name_override", &self.profile_name_override)
66 .finish()
67 }
68}
69
70impl Default for ProviderConfig {
71 fn default() -> Self {
72 Self {
73 env: Env::default(),
74 fs: Fs::default(),
75 time_source: SharedTimeSource::default(),
76 http_client: None,
77 sleep_impl: default_async_sleep(),
78 region: None,
79 use_fips: None,
80 use_dual_stack: None,
81 parsed_profile: Default::default(),
82 #[allow(deprecated)]
83 profile_files: ProfileFiles::default(),
84 profile_name_override: None,
85 }
86 }
87}
88
89#[cfg(test)]
90impl ProviderConfig {
91 pub fn no_configuration() -> Self {
96 use aws_smithy_async::time::StaticTimeSource;
97 use std::collections::HashMap;
98 use std::time::UNIX_EPOCH;
99 let fs = Fs::from_raw_map(HashMap::new());
100 let env = Env::from_slice(&[]);
101 Self {
102 parsed_profile: Default::default(),
103 #[allow(deprecated)]
104 profile_files: ProfileFiles::default(),
105 env,
106 fs,
107 time_source: SharedTimeSource::new(StaticTimeSource::new(UNIX_EPOCH)),
108 http_client: None,
109 sleep_impl: None,
110 region: None,
111 use_fips: None,
112 use_dual_stack: None,
113 profile_name_override: None,
114 }
115 }
116}
117
118impl ProviderConfig {
119 pub fn without_region() -> Self {
141 Self::default()
142 }
143
144 pub fn empty() -> Self {
146 ProviderConfig {
147 env: Env::default(),
148 fs: Fs::default(),
149 time_source: SharedTimeSource::default(),
150 http_client: None,
151 sleep_impl: None,
152 region: None,
153 use_fips: None,
154 use_dual_stack: None,
155 parsed_profile: Default::default(),
156 #[allow(deprecated)]
157 profile_files: ProfileFiles::default(),
158 profile_name_override: None,
159 }
160 }
161
162 pub(crate) fn init(
164 time_source: SharedTimeSource,
165 sleep_impl: Option<SharedAsyncSleep>,
166 ) -> Self {
167 Self {
168 parsed_profile: Default::default(),
169 #[allow(deprecated)]
170 profile_files: ProfileFiles::default(),
171 env: Env::default(),
172 fs: Fs::default(),
173 time_source,
174 http_client: None,
175 sleep_impl,
176 region: None,
177 use_fips: None,
178 use_dual_stack: None,
179 profile_name_override: None,
180 }
181 }
182
183 pub async fn with_default_region() -> Self {
196 Self::without_region().load_default_region().await
197 }
198
199 pub(crate) fn client_config(&self) -> SdkConfig {
200 let mut builder = SdkConfig::builder()
201 .retry_config(RetryConfig::standard())
202 .region(self.region())
203 .time_source(self.time_source())
204 .use_fips(self.use_fips().unwrap_or_default())
205 .use_dual_stack(self.use_dual_stack().unwrap_or_default())
206 .behavior_version(crate::BehaviorVersion::latest());
207 builder.set_http_client(self.http_client.clone());
208 builder.set_sleep_impl(self.sleep_impl.clone());
209 builder.build()
210 }
211
212 #[allow(dead_code)]
215 pub(crate) fn env(&self) -> Env {
216 self.env.clone()
217 }
218
219 #[allow(dead_code)]
220 pub(crate) fn fs(&self) -> Fs {
221 self.fs.clone()
222 }
223
224 #[allow(dead_code)]
225 pub(crate) fn time_source(&self) -> SharedTimeSource {
226 self.time_source.clone()
227 }
228
229 #[allow(dead_code)]
230 pub(crate) fn http_client(&self) -> Option<SharedHttpClient> {
231 self.http_client.clone()
232 }
233
234 #[allow(dead_code)]
235 pub(crate) fn sleep_impl(&self) -> Option<SharedAsyncSleep> {
236 self.sleep_impl.clone()
237 }
238
239 #[allow(dead_code)]
240 pub(crate) fn region(&self) -> Option<Region> {
241 self.region.clone()
242 }
243
244 #[allow(dead_code)]
245 pub(crate) fn use_fips(&self) -> Option<bool> {
246 self.use_fips
247 }
248
249 #[allow(dead_code)]
250 pub(crate) fn use_dual_stack(&self) -> Option<bool> {
251 self.use_dual_stack
252 }
253
254 pub(crate) async fn try_profile(&self) -> Result<&ProfileSet, &ProfileFileLoadError> {
255 let parsed_profile = self
256 .parsed_profile
257 .get_or_init(|| async {
258 let profile = profile::load(
259 &self.fs,
260 &self.env,
261 &self.profile_files,
262 self.profile_name_override.clone(),
263 )
264 .await;
265 if let Err(err) = profile.as_ref() {
266 tracing::warn!(err = %DisplayErrorContext(&err), "failed to parse profile")
267 }
268 profile
269 })
270 .await;
271 parsed_profile.as_ref()
272 }
273
274 pub(crate) async fn profile(&self) -> Option<&ProfileSet> {
275 self.try_profile().await.ok()
276 }
277
278 pub fn with_region(mut self, region: Option<Region>) -> Self {
280 self.region = region;
281 self
282 }
283
284 pub(crate) fn with_use_fips(mut self, use_fips: Option<bool>) -> Self {
286 self.use_fips = use_fips;
287 self
288 }
289
290 pub(crate) fn with_use_dual_stack(mut self, use_dual_stack: Option<bool>) -> Self {
292 self.use_dual_stack = use_dual_stack;
293 self
294 }
295
296 pub(crate) fn with_profile_name(self, profile_name: String) -> Self {
297 let profile_files = self.profile_files.clone();
298 self.with_profile_config(Some(profile_files), Some(profile_name))
299 }
300
301 #[allow(deprecated)]
303 pub(crate) fn with_profile_config(
304 self,
305 profile_files: Option<ProfileFiles>,
306 profile_name_override: Option<String>,
307 ) -> Self {
308 if profile_files.is_none() && profile_name_override.is_none() {
310 return self;
311 }
312 ProviderConfig {
313 parsed_profile: Default::default(),
315 profile_files: profile_files.unwrap_or(self.profile_files),
316 profile_name_override: profile_name_override
317 .map(Cow::Owned)
318 .or(self.profile_name_override),
319 ..self
320 }
321 }
322
323 pub async fn load_default_region(self) -> Self {
328 use crate::default_provider::region::DefaultRegionChain;
329 let provider_chain = DefaultRegionChain::builder().configure(&self).build();
330 self.with_region(provider_chain.region().await)
331 }
332
333 pub(crate) fn with_fs(self, fs: Fs) -> Self {
334 ProviderConfig {
335 parsed_profile: Default::default(),
336 fs,
337 ..self
338 }
339 }
340
341 pub(crate) fn with_env(self, env: Env) -> Self {
342 ProviderConfig {
343 parsed_profile: Default::default(),
344 env,
345 ..self
346 }
347 }
348
349 pub fn with_time_source(self, time_source: impl TimeSource + 'static) -> Self {
351 ProviderConfig {
352 time_source: time_source.into_shared(),
353 ..self
354 }
355 }
356
357 pub fn with_http_client(self, http_client: impl HttpClient + 'static) -> Self {
359 ProviderConfig {
360 http_client: Some(http_client.into_shared()),
361 ..self
362 }
363 }
364
365 pub fn with_sleep_impl(self, sleep_impl: impl AsyncSleep + 'static) -> Self {
367 ProviderConfig {
368 sleep_impl: Some(sleep_impl.into_shared()),
369 ..self
370 }
371 }
372}