schematic/config/
configs.rs

1use super::error::ConfigError;
2#[cfg(feature = "extends")]
3use super::extender::ExtendsFrom;
4#[cfg(feature = "validate")]
5use super::validator::*;
6use schematic_types::Schematic;
7use serde::{Serialize, de::DeserializeOwned};
8use std::collections::BTreeMap;
9
10/// Represents a partial configuration of the base [`Config`], with all settings marked as optional
11/// by wrapping the values in [`Option`].
12pub trait PartialConfig:
13    Clone + Default + DeserializeOwned + Schematic + Serialize + Sized
14{
15    type Context: Default;
16
17    /// Return a partial configuration with values populated with default values for settings
18    /// marked with `#[setting(default)]`. Unmarked settings will be [`None`].
19    ///
20    /// If a default value fails to parse or cast into the correct type, an error is returned.
21    fn default_values(context: &Self::Context) -> Result<Option<Self>, ConfigError>;
22
23    /// Return a partial configuration with values populated from environment variables
24    /// for settings marked with `#[setting(env)]`. Unmarked settings will be [`None`].
25    ///
26    /// If an environment variable does not exist, the value will be [`None`]. If
27    /// the variable fails to parse or cast into the correct type, an error is returned.
28    #[cfg(feature = "env")]
29    fn env_values() -> Result<Option<Self>, ConfigError>;
30
31    /// When a setting is marked as extendable with `#[setting(extend)]`, this returns
32    /// [`ExtendsFrom`] with the extended sources, either a list of strings or a single string.
33    /// When no setting is extendable, this returns [`None`].
34    #[cfg(feature = "extends")]
35    fn extends_from(&self) -> Option<ExtendsFrom>;
36
37    /// Finalize the partial configuration by consuming it and populating all fields with a value.
38    /// Defaults values from [`PartialConfig::default_values`] will be applied first, followed
39    /// by merging the current partial, and lastly environment variable values from
40    /// [`PartialConfig::env_values`].
41    fn finalize(self, context: &Self::Context) -> Result<Self, ConfigError>;
42
43    /// Merge another partial configuration into this one and clone values when applicable. The
44    /// following merge strategies are applied:
45    ///
46    /// - Current [`None`] values are replaced with the next value if [`Some`].
47    /// - Current [`Some`] values are merged with the next value if [`Some`],
48    ///   using the merge function from `#[setting(merge)]`.
49    fn merge(&mut self, context: &Self::Context, next: Self) -> Result<(), ConfigError>;
50
51    /// Recursively validate the configuration with the provided context.
52    /// Validation should be done on the final state, after merging partials.
53    #[cfg(feature = "validate")]
54    fn validate(&self, context: &Self::Context, finalize: bool) -> Result<(), ConfigError> {
55        if let Err(errors) =
56            self.validate_with_path(context, finalize, super::path::Path::default())
57        {
58            return Err(ConfigError::Validator {
59                location: String::new(),
60                error: Box::new(ValidatorError { errors }),
61                help: None,
62            });
63        }
64
65        Ok(())
66    }
67
68    /// Internal use only, use [`validate`] instead.
69    #[cfg(feature = "validate")]
70    #[doc(hidden)]
71    fn validate_with_path(
72        &self,
73        _context: &Self::Context,
74        _finalize: bool,
75        _path: super::path::Path,
76    ) -> Result<(), Vec<ValidateError>> {
77        Ok(())
78    }
79}
80
81/// Represents the final configuration, with all settings populated with a value.
82pub trait Config: Sized + Schematic {
83    type Partial: PartialConfig;
84
85    /// Convert a partial configuration into a full configuration, with all values populated.
86    fn from_partial(partial: Self::Partial) -> Self;
87
88    /// Return a map of all settings and their metadata for the configuration.
89    fn settings() -> ConfigSettingMap {
90        BTreeMap::default()
91    }
92}
93
94/// Represents an enumerable setting for use within a [`Config`].
95pub trait ConfigEnum: Sized + Schematic {
96    /// Return a list of all variants for the enum. Only unit variants are supported.
97    fn variants() -> Vec<Self>;
98}
99
100/// Represents metadata about a setting within a configuration.
101#[derive(Clone, Debug, Default)]
102pub struct ConfigSetting {
103    pub env_key: Option<String>,
104    pub nested: Option<ConfigSettingMap>,
105    pub type_alias: String,
106}
107
108pub type ConfigSettingMap = BTreeMap<String, ConfigSetting>;