Skip to main content

config/
cfg.rs

1use crate::{path, settings, Builder, Reloadable, Section, Settings};
2use arc_swap::ArcSwap;
3use std::collections::VecDeque;
4use std::fmt::{self, Debug, Display, Formatter, Write};
5use std::{any::Any, sync::Arc};
6use tokens::{ChangeToken, CompositeChangeToken, Registration, SharedChangeToken, SingleChangeToken};
7use tracing::{error, trace};
8
9#[cfg(feature = "binder")]
10use {crate::prelude::Binder, serde::de::DeserializeOwned, std::str::FromStr};
11
12/// Represents a configuration.
13#[derive(Clone)]
14pub struct Configuration {
15    pub(crate) settings: Settings,
16    token: SharedChangeToken<CompositeChangeToken>,
17    pub(crate) providers: Vec<String>,
18}
19
20impl Configuration {
21    /// Initializes a new [Configuration].
22    ///
23    /// # Arguments
24    ///
25    /// * `settings` - The configuration [settings](Settings)
26    /// * `tokens` - The [sequence](Iterator) of [change tokens](ChangeToken) associated with the configuration
27    /// * `providers` - The names of the providers that generated the configuration
28    #[inline]
29    pub fn new(
30        settings: Settings,
31        tokens: impl IntoIterator<Item = Box<dyn ChangeToken>>,
32        providers: Vec<String>,
33    ) -> Self {
34        Self {
35            settings,
36            token: SharedChangeToken::new(CompositeChangeToken::new(tokens.into_iter())),
37            providers,
38        }
39    }
40
41    /// Gets a configuration value.
42    ///
43    /// # Arguments
44    ///
45    /// * `key` - The case-insensitive key of the configuration value to get
46    #[inline]
47    pub fn get(&self, key: &str) -> Option<&str> {
48        self.settings.get(key)
49    }
50
51    /// Gets a section in this configuration.
52    ///
53    /// # Arguments
54    ///
55    /// * `key` - The case-insensitive key of the configuration subsection to get
56    #[inline]
57    pub fn section(&self, key: impl Into<String>) -> Section<'_> {
58        Section::new(self, key.into())
59    }
60
61    /// Gets all of the sections in this configuration.
62    pub fn sections(&self) -> Vec<Section<'_>> {
63        let mut keys = Vec::new();
64
65        for (path, _) in self {
66            let Some(key) = path::next(path, None) else {
67                continue;
68            };
69
70            if !keys.iter().any(|k: &String| k.eq_ignore_ascii_case(key)) {
71                keys.push(key.to_owned());
72            }
73        }
74
75        keys.into_iter().map(|key| self.section(key)).collect()
76    }
77
78    /// Returns a [change token](ChangeToken) that indicates when the configuration has changed.
79    #[inline]
80    pub fn change_token(&self) -> impl ChangeToken {
81        self.token.clone()
82    }
83}
84
85impl<'a> IntoIterator for &'a Configuration {
86    type Item = (&'a str, &'a str);
87    type IntoIter = settings::Iter<'a>;
88
89    #[inline]
90    fn into_iter(self) -> Self::IntoIter {
91        (&self.settings).into_iter()
92    }
93}
94
95impl IntoIterator for Configuration {
96    type Item = (String, String);
97    type IntoIter = settings::IntoIter;
98
99    #[inline]
100    fn into_iter(self) -> Self::IntoIter {
101        self.settings.into_iter()
102    }
103}
104
105impl<'a> From<&'a Configuration> for Vec<Section<'a>> {
106    #[inline]
107    fn from(config: &'a Configuration) -> Self {
108        config.sections()
109    }
110}
111
112impl Debug for Configuration {
113    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
114        Debug::fmt(&self.settings, f)
115    }
116}
117
118impl Display for Configuration {
119    #[inline]
120    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
121        let mut sections: VecDeque<_> = self.sections().into_iter().map(|s| (0, s)).collect();
122
123        while let Some((depth, section)) = sections.pop_front() {
124            write!(f, "{:width$}", "", width = depth * 2)?;
125            Display::fmt(&section, f)?;
126
127            for (i, child) in section.sections().into_iter().map(|s| (depth + 1, s)).enumerate() {
128                sections.insert(i, child);
129            }
130
131            if !sections.is_empty() {
132                f.write_char('\n')?;
133            }
134        }
135
136        Ok(())
137    }
138}
139
140fn on_changed(state: Option<Arc<dyn Any + Send + Sync + 'static>>) {
141    let Some(state) = state else {
142        return;
143    };
144
145    let inner = state.downcast_ref::<Inner>().expect("received state other than Inner");
146
147    match inner.builder.build() {
148        Ok(config) => {
149            let registration = inner
150                .config
151                .load()
152                .change_token()
153                .register(Box::new(on_changed), Some(state.clone()));
154            let token = inner.token.swap(Arc::new(SharedChangeToken::default()));
155
156            inner.config.store(Arc::new(config));
157            inner.registration.store(Arc::new(registration));
158
159            trace!("Reloaded the configuration");
160
161            token.notify();
162        }
163        Err(error) => error!("Failed to reload the configuration. {error:?}"),
164    }
165}
166
167struct Inner {
168    builder: Builder,
169    config: ArcSwap<Configuration>,
170    registration: ArcSwap<Registration>,
171    token: ArcSwap<SharedChangeToken<SingleChangeToken>>,
172}
173
174/// Represents a reloadable [configuration](Configuration).
175pub struct ReloadableConfiguration(Arc<Inner>);
176
177impl ReloadableConfiguration {
178    /// Initializes a new [ReloadableConfiguration].
179    ///
180    /// # Arguments
181    ///
182    /// * `builder` - The [builder](Builder) used to reload the configuration
183    /// * `config` - The initial [configuration](Configuration)
184    pub fn new(builder: Builder, configuration: Configuration) -> Self {
185        let inner = Arc::new(Inner {
186            builder,
187            config: ArcSwap::from_pointee(configuration),
188            registration: ArcSwap::from_pointee(Registration::none()),
189            token: ArcSwap::from_pointee(SharedChangeToken::default()),
190        });
191        let registration = inner
192            .config
193            .load()
194            .change_token()
195            .register(Box::new(on_changed), Some(inner.clone()));
196
197        inner.registration.store(registration.into());
198        Self(inner)
199    }
200
201    /// Gets the current [configuration](Configuration).
202    ///
203    /// # Remarks
204    ///
205    /// This method will reload the [configuration](Configuration) if it has changed. If the reload operation fails,
206    /// then the error is logged and the previous [configuration](Configuration) is retained.
207    #[inline]
208    pub fn current(&self) -> Arc<Configuration> {
209        self.0.config.load_full()
210    }
211}
212
213#[cfg(feature = "binder")]
214impl ReloadableConfiguration {
215    /// Creates and returns a structure reified from the configuration.
216    #[inline]
217    pub fn reify<T: DeserializeOwned>(&self) -> crate::Result<T> {
218        self.0.config.load().reify()
219    }
220
221    /// Binds the configuration to the specified instance.
222    ///
223    /// # Arguments
224    ///
225    /// * `instance` - The instance to bind the configuration to
226    #[inline]
227    pub fn bind<T: DeserializeOwned>(&self, instance: &mut T) -> crate::Result {
228        self.0.config.load().bind(instance)
229    }
230
231    /// Binds the specified configuration section to the provided instance.
232    ///
233    /// # Arguments
234    ///
235    /// * `key` - The key of the configuration section to bind
236    /// * `instance` - The instance to bind the configuration to
237    #[inline]
238    pub fn bind_at<T: DeserializeOwned>(&self, key: impl AsRef<str>, instance: &mut T) -> crate::Result {
239        self.0.config.load().bind_at(key, instance)
240    }
241
242    /// Gets a typed value from the configuration.
243    ///
244    /// # Arguments
245    ///
246    /// * `key` - The key of the value to retrieve
247    #[inline]
248    pub fn get_value<T: FromStr>(&self, key: impl AsRef<str>) -> Result<Option<T>, T::Err> {
249        self.0.config.load().get_value(key)
250    }
251
252    /// Gets an optional, typed value from the configuration.
253    ///
254    /// # Arguments
255    ///
256    /// * `key` - The key of the value to retrieve
257    #[inline]
258    pub fn get_value_or_default<T: FromStr + Default>(&self, key: impl AsRef<str>) -> Result<T, T::Err> {
259        self.0.config.load().get_value_or_default(key)
260    }
261}
262
263impl Clone for ReloadableConfiguration {
264    #[inline]
265    fn clone(&self) -> Self {
266        Self(self.0.clone())
267    }
268}
269
270impl Reloadable for ReloadableConfiguration {
271    #[inline]
272    fn can_reload(&self) -> bool {
273        true
274    }
275
276    #[inline]
277    fn reload_token(&self) -> impl ChangeToken + 'static {
278        (**self.0.token.load()).clone()
279    }
280}
281
282impl Debug for ReloadableConfiguration {
283    #[inline]
284    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
285        Debug::fmt(&*self.current(), f)
286    }
287}
288
289impl Display for ReloadableConfiguration {
290    #[inline]
291    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
292        Display::fmt(&*self.current(), f)
293    }
294}
295
296impl From<ReloadableConfiguration> for Arc<Configuration> {
297    #[inline]
298    fn from(rc: ReloadableConfiguration) -> Self {
299        rc.current()
300    }
301}
302
303impl From<&ReloadableConfiguration> for Arc<Configuration> {
304    #[inline]
305    fn from(rc: &ReloadableConfiguration) -> Self {
306        rc.current()
307    }
308}
309
310impl TryFrom<Builder> for ReloadableConfiguration {
311    type Error = crate::Error;
312
313    fn try_from(builder: Builder) -> crate::Result<Self> {
314        let config = builder.build()?;
315        Ok(Self::new(builder, config))
316    }
317}