Skip to main content

configbuilder_pattern_loader/
optional_section.rs

1use crate::{ConfigError, DefaultLoader, FeatureMetadata, FeatureState, OnError};
2
3/// Marks a typed struct as an opt-in TOML feature section.
4///
5/// Unlike [`ConfigSection`], which returns `T::default()` for absent keys,
6/// `OptionalSection` treats absence as "feature disabled" and returns
7/// [`FeatureState::Disabled`] — no defaults, no error.
8///
9/// [`ConfigSection`]: crate::ConfigSection
10pub trait OptionalSection: serde::de::DeserializeOwned + Send + Sync + 'static {
11    /// The top-level TOML key for this section (e.g. `"message_broker"`).
12    fn section_name() -> &'static str;
13
14    /// Other section keys this feature requires to be enabled before it can run.
15    ///
16    /// Checked by [`crate::FeatureRegistryOps::validate_dependencies`] after all
17    /// features are loaded. Default: no dependencies (`&[]`).
18    fn requires() -> &'static [&'static str] {
19        &[]
20    }
21
22    /// What the feature registry should do when [`validate_enabled`] returns an error.
23    ///
24    /// - [`OnError::Fail`] *(default)* — propagate the error; startup halts.
25    /// - [`OnError::Disable`] — treat the feature as disabled and continue startup.
26    ///
27    /// [`validate_enabled`]: OptionalSection::validate_enabled
28    fn on_error() -> OnError {
29        OnError::Fail
30    }
31
32    /// Static documentation and ownership annotations for this feature.
33    fn metadata() -> FeatureMetadata {
34        FeatureMetadata::default()
35    }
36
37    /// Validate cross-field constraints after the section has been deserialised.
38    ///
39    /// Only called when the section key is present in TOML. Default: always `Ok(())`.
40    fn validate_enabled(&self) -> Result<(), ConfigError> {
41        Ok(())
42    }
43
44    /// Load this section as an optional feature from `loader`.
45    ///
46    /// Returns `FeatureState::Enabled(Self)` when the key is present and valid,
47    /// `FeatureState::Disabled` when absent, and `Err` for I/O, parse, or
48    /// validation failures.
49    ///
50    /// **Note:** [`on_error`] and [`requires`] are only applied when loading
51    /// through [`crate::FeatureRegistryOps::load`]. Use that for full startup
52    /// coordination with graceful degradation and dependency validation.
53    ///
54    /// [`on_error`]: OptionalSection::on_error
55    /// [`requires`]: OptionalSection::requires
56    fn load_optional(loader: &DefaultLoader) -> Result<FeatureState<Self>, ConfigError>
57    where
58        Self: Sized,
59    {
60        let state: FeatureState<Self> = loader.load_optional_section(Self::section_name())?;
61        if let FeatureState::Enabled(ref value) = state {
62            value.validate_enabled()?;
63        }
64        Ok(state)
65    }
66}