aws_smithy_runtime/client/
config_override.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use aws_smithy_async::rt::sleep::SharedAsyncSleep;
7use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder;
8use aws_smithy_types::config_bag::{
9    CloneableLayer, FrozenLayer, Layer, Storable, Store, StoreReplace,
10};
11
12macro_rules! component {
13    ($typ:ty, $accessor:ident, $latest_accessor:ident, $doc:tt) => {
14        #[doc = $doc]
15        pub fn $accessor(&self) -> Option<$typ> {
16            fallback_component!(self, $typ, $accessor)
17        }
18
19        #[doc = $doc]
20        pub fn $latest_accessor(&self) -> Option<$typ> {
21            latest_component!(self, $typ, $accessor)
22        }
23    };
24}
25macro_rules! fallback_component {
26    ($self:ident, $typ:ty, $accessor:ident) => {
27        match &$self.inner {
28            Inner::Initial(initial) => initial.components.$accessor(),
29            Inner::Override(overrid) => overrid
30                .components
31                .$accessor()
32                .or_else(|| overrid.initial_components.$accessor()),
33        }
34    };
35}
36macro_rules! latest_component {
37    ($self:ident, $typ:ty, $accessor:ident) => {
38        match &$self.inner {
39            Inner::Initial(initial) => initial.components.$accessor(),
40            Inner::Override(overrid) => overrid.components.$accessor(),
41        }
42    };
43}
44
45struct Initial<'a> {
46    config: &'a mut CloneableLayer,
47    components: &'a mut RuntimeComponentsBuilder,
48}
49
50struct Override<'a> {
51    initial_config: FrozenLayer,
52    initial_components: &'a RuntimeComponentsBuilder,
53    config: &'a mut CloneableLayer,
54    components: &'a mut RuntimeComponentsBuilder,
55}
56
57enum Inner<'a> {
58    Initial(Initial<'a>),
59    Override(Override<'a>),
60}
61
62/// Utility to simplify config building and config overrides.
63///
64/// The resolver allows the same initialization logic to be reused
65/// for both initial config and override config.
66///
67/// This resolver can be initialized to one of two modes:
68/// 1. _Initial mode_: The resolver is being used in a service `Config` builder's `build()` method, and thus,
69///    there is no config override at this point.
70/// 2. _Override mode_: The resolver is being used by the `ConfigOverrideRuntimePlugin`'s constructor and needs
71///    to incorporate both the original config and the given config override for this operation.
72///
73/// In all the methods on [`Resolver`], the term "latest" refers to the initial config when in _Initial mode_,
74/// and to config override when in _Override mode_.
75pub struct Resolver<'a> {
76    inner: Inner<'a>,
77}
78
79impl<'a> Resolver<'a> {
80    /// Construct a new [`Resolver`] in _initial mode_.
81    pub fn initial(
82        config: &'a mut CloneableLayer,
83        components: &'a mut RuntimeComponentsBuilder,
84    ) -> Self {
85        Self {
86            inner: Inner::Initial(Initial { config, components }),
87        }
88    }
89
90    /// Construct a new [`Resolver`] in _override mode_.
91    pub fn overrid(
92        initial_config: FrozenLayer,
93        initial_components: &'a RuntimeComponentsBuilder,
94        config: &'a mut CloneableLayer,
95        components: &'a mut RuntimeComponentsBuilder,
96    ) -> Self {
97        Self {
98            inner: Inner::Override(Override {
99                initial_config,
100                initial_components,
101                config,
102                components,
103            }),
104        }
105    }
106
107    /// Returns true if in _initial mode_.
108    pub fn is_initial(&self) -> bool {
109        matches!(self.inner, Inner::Initial(_))
110    }
111
112    /// Returns a mutable reference to the latest config.
113    pub fn config_mut(&mut self) -> &mut CloneableLayer {
114        match &mut self.inner {
115            Inner::Initial(initial) => initial.config,
116            Inner::Override(overrid) => overrid.config,
117        }
118    }
119
120    /// Returns a mutable reference to the latest runtime components.
121    pub fn runtime_components_mut(&mut self) -> &mut RuntimeComponentsBuilder {
122        match &mut self.inner {
123            Inner::Initial(initial) => initial.components,
124            Inner::Override(overrid) => overrid.components,
125        }
126    }
127
128    /// Returns true if the latest config has `T` set.
129    ///
130    /// The "latest" is initial for `Resolver::Initial`, and override for `Resolver::Override`.
131    pub fn is_latest_set<T>(&self) -> bool
132    where
133        T: Storable<Storer = StoreReplace<T>>,
134    {
135        self.config().load::<T>().is_some()
136    }
137
138    /// Returns true if `T` is set anywhere.
139    pub fn is_set<T>(&self) -> bool
140    where
141        T: Storable<Storer = StoreReplace<T>>,
142    {
143        match &self.inner {
144            Inner::Initial(initial) => initial.config.load::<T>().is_some(),
145            Inner::Override(overrid) => {
146                overrid.initial_config.load::<T>().is_some() || overrid.config.load::<T>().is_some()
147            }
148        }
149    }
150
151    /// Resolves the value `T` with fallback
152    pub fn resolve_config<T>(&self) -> <T::Storer as Store>::ReturnedType<'_>
153    where
154        T: Storable<Storer = StoreReplace<T>>,
155    {
156        let mut maybe_value = self.config().load::<T>();
157        if maybe_value.is_none() {
158            // Try to fallback
159            if let Inner::Override(overrid) = &self.inner {
160                maybe_value = overrid.initial_config.load::<T>()
161            }
162        }
163        maybe_value
164    }
165
166    // Add additional component methods as needed
167    component!(
168        SharedAsyncSleep,
169        sleep_impl,
170        latest_sleep_impl,
171        "The async sleep implementation."
172    );
173
174    fn config(&self) -> &Layer {
175        match &self.inner {
176            Inner::Initial(initial) => initial.config,
177            Inner::Override(overrid) => overrid.config,
178        }
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185    use aws_smithy_types::config_bag::CloneableLayer;
186
187    #[derive(Clone, Debug)]
188    struct TestStorable(String);
189    impl Storable for TestStorable {
190        type Storer = StoreReplace<Self>;
191    }
192
193    #[test]
194    fn initial_mode_config() {
195        let mut config = CloneableLayer::new("test");
196        let mut components = RuntimeComponentsBuilder::new("test");
197
198        let mut resolver = Resolver::initial(&mut config, &mut components);
199        assert!(resolver.is_initial());
200        assert!(!resolver.is_latest_set::<TestStorable>());
201        assert!(!resolver.is_set::<TestStorable>());
202        assert!(resolver.resolve_config::<TestStorable>().is_none());
203
204        resolver.config_mut().store_put(TestStorable("test".into()));
205        assert!(resolver.is_latest_set::<TestStorable>());
206        assert!(resolver.is_set::<TestStorable>());
207        assert_eq!("test", resolver.resolve_config::<TestStorable>().unwrap().0);
208    }
209
210    #[test]
211    fn override_mode_config() {
212        let mut initial_config = CloneableLayer::new("initial");
213        let initial_components = RuntimeComponentsBuilder::new("initial");
214        let mut config = CloneableLayer::new("override");
215        let mut components = RuntimeComponentsBuilder::new("override");
216
217        let resolver = Resolver::overrid(
218            initial_config.clone().freeze(),
219            &initial_components,
220            &mut config,
221            &mut components,
222        );
223        assert!(!resolver.is_initial());
224        assert!(!resolver.is_latest_set::<TestStorable>());
225        assert!(!resolver.is_set::<TestStorable>());
226        assert!(resolver.resolve_config::<TestStorable>().is_none());
227
228        initial_config.store_put(TestStorable("test".into()));
229        let resolver = Resolver::overrid(
230            initial_config.clone().freeze(),
231            &initial_components,
232            &mut config,
233            &mut components,
234        );
235        assert!(!resolver.is_latest_set::<TestStorable>());
236        assert!(resolver.is_set::<TestStorable>());
237        assert_eq!("test", resolver.resolve_config::<TestStorable>().unwrap().0);
238
239        initial_config.unset::<TestStorable>();
240        config.store_put(TestStorable("test".into()));
241        let resolver = Resolver::overrid(
242            initial_config.clone().freeze(),
243            &initial_components,
244            &mut config,
245            &mut components,
246        );
247        assert!(resolver.is_latest_set::<TestStorable>());
248        assert!(resolver.is_set::<TestStorable>());
249        assert_eq!("test", resolver.resolve_config::<TestStorable>().unwrap().0);
250
251        initial_config.store_put(TestStorable("override me".into()));
252        config.store_put(TestStorable("override".into()));
253        let resolver = Resolver::overrid(
254            initial_config.freeze(),
255            &initial_components,
256            &mut config,
257            &mut components,
258        );
259        assert!(resolver.is_latest_set::<TestStorable>());
260        assert!(resolver.is_set::<TestStorable>());
261        assert_eq!(
262            "override",
263            resolver.resolve_config::<TestStorable>().unwrap().0
264        );
265    }
266}