1pub 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 #[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}