Skip to main content

elfo_core/
config.rs

1//! Contains useful utilities for working with configuration.
2//! [The Actoromicon](https://actoromicon.rs/ch03-02-configuration.html).
3//!
4//! Also contains [`system`] to describe system configuration.
5
6use std::{
7    any::{Any, TypeId},
8    fmt, mem,
9    ops::Deref,
10    str::FromStr,
11    sync::Arc,
12};
13
14use derive_more::From;
15use serde::{de, de::value::Error as DeError, Deserialize, Deserializer, Serialize, Serializer};
16use serde_value::{Value, ValueDeserializer};
17
18use crate::{local::Local, panic};
19
20/// Represents any user-defined config.
21///
22/// It's implemented automatically for any `Deserialize + Send + Sync + Debug`.
23pub trait Config: for<'de> Deserialize<'de> + Send + Sync + fmt::Debug + 'static {}
24impl<C> Config for C where C: for<'de> Deserialize<'de> + Send + Sync + fmt::Debug + 'static {}
25
26assert_impl_all!((): Config);
27
28// === AnyConfig ===
29
30/// Holds user-defined config.
31///
32/// Usually not created directly outside tests sending [`ValidateConfig`] or
33/// [`UpdateConfig`] messages.
34///
35/// [`ValidateConfig`]: crate::messages::ValidateConfig
36/// [`UpdateConfig`]: crate::messages::UpdateConfig
37///
38/// # Example
39/// In tests it can be used in the following way:
40/// ```
41/// # use serde::Deserialize;
42/// # use toml::toml;
43/// # use elfo_core::config::AnyConfig;
44/// AnyConfig::deserialize(toml! {
45///     some_param = 10
46/// });
47/// ```
48#[derive(Clone)]
49pub struct AnyConfig {
50    raw: Arc<Value>,
51    decoded: Option<Local<Decoded>>,
52}
53
54#[derive(Clone)]
55struct Decoded {
56    system: Arc<SystemConfig>,
57    // Actually, we store `Arc<Arc<C>>` here.
58    user: Arc<dyn Any + Send + Sync>,
59}
60
61impl AnyConfig {
62    /// Creates `AnyConfig` from `serde_value::Value`.
63    ///
64    /// This method is unstable because it relies on the specific implementation
65    /// using `serde_value`. `AnyConfig::deserialize` should be used instead
66    /// where possible.
67    #[instability::unstable]
68    pub fn from_value(value: Value) -> Self {
69        Self {
70            raw: Arc::new(value),
71            decoded: None,
72        }
73    }
74
75    pub(crate) fn get_user<C: 'static>(&self) -> &Arc<C> {
76        self.decoded
77            .as_ref()
78            .and_then(|local| local.user.downcast_ref())
79            .expect("must be decoded")
80    }
81
82    pub(crate) fn get_system(&self) -> &Arc<SystemConfig> {
83        &self.decoded.as_ref().expect("must be decoded").system
84    }
85
86    pub(crate) fn decode<C: Config>(&self) -> Result<AnyConfig, String> {
87        match panic::sync_catch(|| self.do_decode::<C>()) {
88            Ok(Ok(config)) => Ok(config),
89            Ok(Err(err)) => Err(err),
90            Err(panic) => Err(panic),
91        }
92    }
93
94    fn do_decode<C: Config>(&self) -> Result<AnyConfig, String> {
95        let mut raw = (*self.raw).clone();
96
97        let system_decoded = if let Value::Map(map) = &mut raw {
98            if let Some(system_raw) = map.remove(&Value::String("system".into())) {
99                let de = ValueDeserializer::<DeError>::new(system_raw);
100                let config = SystemConfig::deserialize(de).map_err(|err| err.to_string())?;
101                Arc::new(config)
102            } else {
103                Default::default()
104            }
105        } else {
106            Default::default()
107        };
108
109        // Handle the special case of default config.
110        let user_decoded = if TypeId::of::<C>() == TypeId::of::<()>() {
111            Arc::new(Arc::new(())) as Arc<_>
112        } else {
113            let de = ValueDeserializer::<DeError>::new(raw);
114            let config = C::deserialize(de).map_err(|err| err.to_string())?;
115            Arc::new(Arc::new(config)) as Arc<_>
116        };
117
118        Ok(AnyConfig {
119            raw: self.raw.clone(),
120            decoded: Some(Local::from(Decoded {
121                system: system_decoded,
122                user: user_decoded,
123            })),
124        })
125    }
126
127    pub(crate) fn into_value(mut self) -> Value {
128        mem::replace(Arc::make_mut(&mut self.raw), Value::Unit)
129    }
130}
131
132impl Default for AnyConfig {
133    fn default() -> Self {
134        Self::from_value(Value::Map(Default::default()))
135    }
136}
137
138impl fmt::Debug for AnyConfig {
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        // Configs can contain credentials, so we should never print unknown configs.
141        f.write_str("..")
142    }
143}
144
145impl<'de> Deserialize<'de> for AnyConfig {
146    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
147        Value::deserialize(deserializer).map(Self::from_value)
148    }
149}
150
151impl Serialize for AnyConfig {
152    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
153        self.raw.serialize(serializer)
154    }
155}
156
157impl<'de> Deserializer<'de> for AnyConfig {
158    type Error = serde_value::DeserializerError;
159
160    serde::forward_to_deserialize_any! {
161        bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit
162        seq bytes byte_buf map unit_struct
163        tuple_struct struct tuple ignored_any identifier
164    }
165
166    fn deserialize_any<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
167        self.into_value().deserialize_any(visitor)
168    }
169
170    fn deserialize_option<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
171        self.into_value().deserialize_option(visitor)
172    }
173
174    fn deserialize_enum<V: de::Visitor<'de>>(
175        self,
176        name: &'static str,
177        variants: &'static [&'static str],
178        visitor: V,
179    ) -> Result<V::Value, Self::Error> {
180        self.into_value().deserialize_enum(name, variants, visitor)
181    }
182
183    fn deserialize_newtype_struct<V: de::Visitor<'de>>(
184        self,
185        name: &'static str,
186        visitor: V,
187    ) -> Result<V::Value, Self::Error> {
188        self.into_value().deserialize_newtype_struct(name, visitor)
189    }
190}
191
192// === SystemConfig ===
193
194pub mod system {
195    //! System (`system.*` in TOML) configuration. [Config].
196    //!
197    //! Note: all types here are exported only for documentation purposes
198    //! and are not subject to stable guarantees. However, the config
199    //! structure (usually encoded in TOML) follows stable guarantees.
200    //!
201    //! [Config]: SystemConfig
202
203    use super::*;
204
205    pub use crate::{
206        dumping::config as dumping, logging::config as logging, mailbox::config as mailbox,
207        restarting::config as restart_policy, telemetry::config as telemetry,
208    };
209
210    /// The `system.*` section in configs.
211    ///
212    /// # Example
213    /// ```toml
214    /// [some_group]
215    /// system.mailbox.capacity = 1000
216    /// system.logging.max_level = "Warn"
217    /// system.dumping.max_rate = 10_000
218    /// system.telemetry.per_actor_key = true
219    /// system.restart_policy.when = "Never"
220    /// ```
221    #[derive(Debug, Default, Deserialize)]
222    #[serde(default)]
223    pub struct SystemConfig {
224        /// Mailbox configuration.
225        pub mailbox: mailbox::MailboxConfig,
226        /// Logging configuration.
227        pub logging: logging::LoggingConfig,
228        /// Dumping configuration.
229        pub dumping: dumping::DumpingConfig,
230        /// Telemetry configuration.
231        pub telemetry: telemetry::TelemetryConfig,
232        /// Restarting configuration.
233        pub restart_policy: restart_policy::RestartPolicyConfig,
234    }
235}
236
237pub(crate) use system::SystemConfig;
238
239// === Secret ===
240
241/// A secret value that is not printed in logs or debug output.
242/// So, it's useful for storing sensitive data like credentials.
243///
244/// * `Debug` and `Display` instances prints `<secret>` instead of real value.
245/// * `Deserialize` expects a real value.
246/// * `Serialize` depends on the current [serde mode]:
247///   * In the `Network` mode it's serialized as the real value.
248///   * In the `Dumping` and `Normal` modes it's serialized as `"<secret>"`.
249///
250/// [serde mode]: crate::scope::with_serde_mode
251///
252/// # Example
253/// ```
254/// # use serde::Deserialize;
255/// # use elfo_core::config::Secret;
256/// #[derive(Deserialize)]
257/// struct MyConfig {
258///     credentials: Secret<String>,
259/// }
260/// ```
261#[derive(Clone, Copy, PartialEq, Eq, Default, From)]
262pub struct Secret<T>(T);
263
264impl<T> Secret<T> {
265    pub fn into_inner(self) -> T {
266        self.0
267    }
268}
269
270impl<T> Deref for Secret<T> {
271    type Target = T;
272
273    #[inline]
274    fn deref(&self) -> &Self::Target {
275        &self.0
276    }
277}
278
279impl<T> fmt::Debug for Secret<T> {
280    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281        write!(f, "<secret>")
282    }
283}
284
285impl<T> fmt::Display for Secret<T> {
286    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287        write!(f, "<secret>")
288    }
289}
290
291impl<T: FromStr> FromStr for Secret<T> {
292    type Err = T::Err;
293
294    fn from_str(s: &str) -> Result<Self, Self::Err> {
295        T::from_str(s).map(Self)
296    }
297}
298
299impl<'de, T: Deserialize<'de>> Deserialize<'de> for Secret<T> {
300    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
301        T::deserialize(deserializer).map(Self)
302    }
303}
304
305impl<T: Serialize> Serialize for Secret<T> {
306    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
307        if crate::scope::serde_mode() != crate::scope::SerdeMode::Network {
308            serializer.serialize_str("<secret>")
309        } else {
310            self.0.serialize(serializer)
311        }
312    }
313}