aws_config/meta/
region.rs1use aws_types::region::Region;
9use std::borrow::Cow;
10use std::fmt::Debug;
11use tracing::Instrument;
12
13#[derive(Debug)]
31pub struct RegionProviderChain {
32 providers: Vec<Box<dyn ProvideRegion>>,
33}
34
35impl RegionProviderChain {
36 pub async fn region(&self) -> Option<Region> {
40 for provider in &self.providers {
41 if let Some(region) = provider
42 .region()
43 .instrument(tracing::info_span!("load_region", provider = ?provider))
44 .await
45 {
46 return Some(region);
47 }
48 }
49 None
50 }
51
52 pub fn first_try(provider: impl ProvideRegion + 'static) -> Self {
54 RegionProviderChain {
55 providers: vec![Box::new(provider)],
56 }
57 }
58
59 pub fn or_else(mut self, fallback: impl ProvideRegion + 'static) -> Self {
61 self.providers.push(Box::new(fallback));
62 self
63 }
64
65 pub fn default_provider() -> Self {
67 Self::first_try(crate::default_provider::region::default_provider())
68 }
69
70 pub fn or_default_provider(mut self) -> Self {
72 self.providers
73 .push(Box::new(crate::default_provider::region::default_provider()));
74 self
75 }
76}
77
78impl ProvideRegion for Option<Region> {
79 fn region(&self) -> future::ProvideRegion<'_> {
80 future::ProvideRegion::ready(self.clone())
81 }
82}
83
84impl ProvideRegion for RegionProviderChain {
85 fn region(&self) -> future::ProvideRegion<'_> {
86 future::ProvideRegion::new(RegionProviderChain::region(self))
87 }
88}
89
90pub mod future {
94 use std::future::Future;
95 use std::pin::Pin;
96 use std::task::{Context, Poll};
97
98 use aws_smithy_async::future::now_or_later::NowOrLater;
99
100 use aws_types::region::Region;
101
102 type BoxFuture<'a> = Pin<Box<dyn Future<Output = Option<Region>> + Send + 'a>>;
103 #[derive(Debug)]
108 pub struct ProvideRegion<'a>(NowOrLater<Option<Region>, BoxFuture<'a>>);
109 impl<'a> ProvideRegion<'a> {
110 pub fn new(future: impl Future<Output = Option<Region>> + Send + 'a) -> Self {
112 Self(NowOrLater::new(Box::pin(future)))
113 }
114
115 pub fn ready(region: Option<Region>) -> Self {
117 Self(NowOrLater::ready(region))
118 }
119 }
120
121 impl Future for ProvideRegion<'_> {
122 type Output = Option<Region>;
123
124 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
125 Pin::new(&mut self.0).poll(cx)
126 }
127 }
128}
129
130pub trait ProvideRegion: Send + Sync + Debug {
135 fn region(&self) -> future::ProvideRegion<'_>;
137}
138
139impl ProvideRegion for Region {
140 fn region(&self) -> future::ProvideRegion<'_> {
141 future::ProvideRegion::ready(Some(self.clone()))
142 }
143}
144
145impl<'a> ProvideRegion for &'a Region {
146 fn region(&self) -> future::ProvideRegion<'_> {
147 future::ProvideRegion::ready(Some((*self).clone()))
148 }
149}
150
151impl ProvideRegion for Box<dyn ProvideRegion> {
152 fn region(&self) -> future::ProvideRegion<'_> {
153 self.as_ref().region()
154 }
155}
156
157impl ProvideRegion for &'static str {
158 fn region(&self) -> future::ProvideRegion<'_> {
159 future::ProvideRegion::ready(Some(Region::new(Cow::Borrowed(*self))))
160 }
161}
162
163#[cfg(test)]
164mod test {
165 use crate::meta::region::RegionProviderChain;
166 use aws_types::region::Region;
167 use futures_util::FutureExt;
168
169 #[test]
170 fn provider_chain() {
171 let a = None;
172 let b = Some(Region::new("us-east-1"));
173 let chain = RegionProviderChain::first_try(a).or_else(b);
174 assert_eq!(
175 chain.region().now_or_never().expect("ready"),
176 Some(Region::new("us-east-1"))
177 );
178 }
179
180 #[test]
181 fn empty_chain() {
182 let chain = RegionProviderChain::first_try(None).or_else(None);
183 assert_eq!(chain.region().now_or_never().expect("ready"), None);
184 }
185}