config/
source.rs

1use std::fmt::Debug;
2use std::str::FromStr;
3
4#[cfg(feature = "async")]
5use async_trait::async_trait;
6
7use crate::error::Result;
8use crate::map::Map;
9use crate::path;
10use crate::value::{Value, ValueKind};
11
12/// Describes a generic _source_ of configuration properties.
13pub trait Source: Debug {
14    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync>;
15
16    /// Collect all configuration properties available from this source into
17    /// a [`Map`].
18    fn collect(&self) -> Result<Map<String, Value>>;
19
20    /// Collects all configuration properties to a provided cache.
21    fn collect_to(&self, cache: &mut Value) -> Result<()> {
22        self.collect()?
23            .into_iter()
24            .for_each(|(key, val)| set_value(cache, key, val));
25
26        Ok(())
27    }
28}
29
30fn set_value(cache: &mut Value, key: String, value: Value) {
31    match path::Expression::from_str(key.as_str()) {
32        // Set using the path
33        Ok(expr) => expr.set(cache, value),
34
35        // Set directly anyway
36        _ => path::Expression::root(key).set(cache, value),
37    }
38}
39
40/// Describes a generic _source_ of configuration properties capable of using an async runtime.
41///
42/// At the moment this library does not implement it, although it allows using its implementations
43/// within builders.  Due to the scattered landscape of asynchronous runtimes, it is impossible to
44/// cater to all needs with one implementation.  Also, this trait might be most useful with remote
45/// configuration sources, reachable via the network, probably using HTTP protocol.  Numerous HTTP
46/// libraries exist, making it even harder to find one implementation that rules them all.
47///
48/// For those reasons, it is left to other crates to implement runtime-specific or proprietary
49/// details.
50///
51/// It is advised to use `async_trait` crate while implementing this trait.
52///
53/// See examples for sample implementation.
54#[cfg(feature = "async")]
55#[async_trait]
56pub trait AsyncSource: Debug + Sync {
57    // Sync is supertrait due to https://docs.rs/async-trait/0.1.50/async_trait/index.html#dyn-traits
58
59    /// Collects all configuration properties available from this source and return
60    /// a Map as an async operations.
61    async fn collect(&self) -> Result<Map<String, Value>>;
62
63    /// Collects all configuration properties to a provided cache.
64    async fn collect_to(&self, cache: &mut Value) -> Result<()> {
65        self.collect()
66            .await?
67            .into_iter()
68            .for_each(|(key, val)| set_value(cache, key, val));
69
70        Ok(())
71    }
72}
73
74#[cfg(feature = "async")]
75impl Clone for Box<dyn AsyncSource + Send + Sync> {
76    fn clone(&self) -> Self {
77        self.to_owned()
78    }
79}
80
81impl Clone for Box<dyn Source + Send + Sync> {
82    fn clone(&self) -> Self {
83        self.clone_into_box()
84    }
85}
86
87impl Source for Vec<Box<dyn Source + Send + Sync>> {
88    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
89        Box::new((*self).clone())
90    }
91
92    fn collect(&self) -> Result<Map<String, Value>> {
93        let mut cache: Value = Map::<String, Value>::new().into();
94
95        for source in self {
96            source.collect_to(&mut cache)?;
97        }
98
99        if let ValueKind::Table(table) = cache.kind {
100            Ok(table)
101        } else {
102            unreachable!();
103        }
104    }
105}
106
107impl Source for [Box<dyn Source + Send + Sync>] {
108    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
109        Box::new(self.to_owned())
110    }
111
112    fn collect(&self) -> Result<Map<String, Value>> {
113        let mut cache: Value = Map::<String, Value>::new().into();
114
115        for source in self {
116            source.collect_to(&mut cache)?;
117        }
118
119        if let ValueKind::Table(table) = cache.kind {
120            Ok(table)
121        } else {
122            unreachable!();
123        }
124    }
125}
126
127impl<T> Source for Vec<T>
128where
129    T: Source + Sync + Send + Clone + 'static,
130{
131    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
132        Box::new((*self).clone())
133    }
134
135    fn collect(&self) -> Result<Map<String, Value>> {
136        let mut cache: Value = Map::<String, Value>::new().into();
137
138        for source in self {
139            source.collect_to(&mut cache)?;
140        }
141
142        if let ValueKind::Table(table) = cache.kind {
143            Ok(table)
144        } else {
145            unreachable!();
146        }
147    }
148}