aws_config/
profile.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Load configuration from AWS Profiles
7//!
8//! AWS profiles are typically stored in `~/.aws/config` and `~/.aws/credentials`. For more details
9//! see the [`load`] function.
10
11pub mod parser;
12
13pub mod credentials;
14pub mod profile_file;
15pub mod region;
16
17#[cfg(feature = "sso")]
18pub mod token;
19#[cfg(feature = "sso")]
20#[doc(inline)]
21pub use token::ProfileFileTokenProvider;
22
23#[doc(inline)]
24pub use aws_runtime::env_config::error::EnvConfigFileLoadError as ProfileFileLoadError;
25#[doc(inline)]
26pub use aws_runtime::env_config::parse::EnvConfigParseError as ProfileParseError;
27#[doc(inline)]
28pub use aws_runtime::env_config::property::Property;
29#[doc(inline)]
30pub use aws_runtime::env_config::section::{EnvConfigSections as ProfileSet, Profile};
31#[doc(inline)]
32pub use credentials::ProfileFileCredentialsProvider;
33#[doc(inline)]
34pub use parser::load;
35#[doc(inline)]
36pub use region::ProfileFileRegionProvider;
37
38mod cell {
39    use std::future::Future;
40    use std::sync::{Arc, Mutex};
41    use tokio::sync::OnceCell;
42
43    /// Once cell with a result where the error can be taken.
44    ///
45    /// The profile providers need to cache their inner provider value (specifically for SSO)
46    /// in order to preserve the SSO token cache. This wrapper around [`OnceCell`] allows
47    /// for initializing the inner provider once in a way that if it fails, the error can
48    /// be taken so that it doesn't need to implement `Clone`.
49    #[derive(Debug)]
50    pub(super) struct ErrorTakingOnceCell<T, E> {
51        cell: OnceCell<Result<Arc<T>, Mutex<E>>>,
52    }
53
54    impl<T, E> ErrorTakingOnceCell<T, E> {
55        pub(super) fn new() -> Self {
56            Self {
57                cell: OnceCell::new(),
58            }
59        }
60
61        pub(super) async fn get_or_init<F, Fut>(
62            &self,
63            init: F,
64            mut taken_error: E,
65        ) -> Result<Arc<T>, E>
66        where
67            F: FnOnce() -> Fut,
68            Fut: Future<Output = Result<T, E>>,
69        {
70            let init = || async move { (init)().await.map(Arc::new).map_err(Mutex::new) };
71            match self.cell.get_or_init(init).await {
72                Ok(value) => Ok(value.clone()),
73                Err(err) => {
74                    let mut locked = err.lock().unwrap();
75                    std::mem::swap(&mut *locked, &mut taken_error);
76                    Err(taken_error)
77                }
78            }
79        }
80    }
81
82    #[cfg(test)]
83    mod tests {
84        use crate::profile::cell::ErrorTakingOnceCell;
85        use std::sync::{
86            atomic::{AtomicUsize, Ordering},
87            Arc,
88        };
89
90        #[derive(Debug)]
91        enum Error {
92            InitError,
93            Taken,
94        }
95
96        #[tokio::test]
97        async fn taken_error() {
98            let cell = ErrorTakingOnceCell::new();
99            let calls = AtomicUsize::new(0);
100            let init = || async {
101                calls.fetch_add(1, Ordering::SeqCst);
102                Result::<String, _>::Err(Error::InitError)
103            };
104
105            let result = cell.get_or_init(init, Error::Taken).await;
106            assert!(matches!(result, Err(Error::InitError)));
107
108            let result = cell.get_or_init(init, Error::Taken).await;
109            assert!(matches!(result, Err(Error::Taken)));
110
111            let result = cell.get_or_init(init, Error::Taken).await;
112            assert!(matches!(result, Err(Error::Taken)));
113            assert_eq!(1, calls.load(Ordering::SeqCst));
114        }
115
116        #[tokio::test]
117        async fn value_initialized_once() {
118            let cell = ErrorTakingOnceCell::new();
119            let calls = AtomicUsize::new(0);
120            let init = || async {
121                calls.fetch_add(1, Ordering::SeqCst);
122                Result::<_, Error>::Ok("test".to_string())
123            };
124
125            let original = cell.get_or_init(init, Error::Taken).await.unwrap();
126            let next = cell.get_or_init(init, Error::Taken).await.unwrap();
127            assert!(Arc::ptr_eq(&original, &next));
128            assert_eq!(1, calls.load(Ordering::SeqCst));
129        }
130    }
131}