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::debug_span!("region_provider_chain", 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 ProvideRegion for &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}