storm_config/
source.rs

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