configbuilder-pattern-loader 0.1.0

configbuilder-pattern: loader domain, trait contract + tech-agnostic reference implementation.
Documentation
use crate::{ConfigError, DefaultLoader, FeatureMetadata, FeatureState, OnError};

/// Marks a typed struct as an opt-in TOML feature section.
///
/// Unlike [`ConfigSection`], which returns `T::default()` for absent keys,
/// `OptionalSection` treats absence as "feature disabled" and returns
/// [`FeatureState::Disabled`] — no defaults, no error.
///
/// [`ConfigSection`]: crate::ConfigSection
pub trait OptionalSection: serde::de::DeserializeOwned + Send + Sync + 'static {
    /// The top-level TOML key for this section (e.g. `"message_broker"`).
    fn section_name() -> &'static str;

    /// Other section keys this feature requires to be enabled before it can run.
    ///
    /// Checked by [`crate::FeatureRegistryOps::validate_dependencies`] after all
    /// features are loaded. Default: no dependencies (`&[]`).
    fn requires() -> &'static [&'static str] {
        &[]
    }

    /// What the feature registry should do when [`validate_enabled`] returns an error.
    ///
    /// - [`OnError::Fail`] *(default)* — propagate the error; startup halts.
    /// - [`OnError::Disable`] — treat the feature as disabled and continue startup.
    ///
    /// [`validate_enabled`]: OptionalSection::validate_enabled
    fn on_error() -> OnError {
        OnError::Fail
    }

    /// Static documentation and ownership annotations for this feature.
    fn metadata() -> FeatureMetadata {
        FeatureMetadata::default()
    }

    /// Validate cross-field constraints after the section has been deserialised.
    ///
    /// Only called when the section key is present in TOML. Default: always `Ok(())`.
    fn validate_enabled(&self) -> Result<(), ConfigError> {
        Ok(())
    }

    /// Load this section as an optional feature from `loader`.
    ///
    /// Returns `FeatureState::Enabled(Self)` when the key is present and valid,
    /// `FeatureState::Disabled` when absent, and `Err` for I/O, parse, or
    /// validation failures.
    ///
    /// **Note:** [`on_error`] and [`requires`] are only applied when loading
    /// through [`crate::FeatureRegistryOps::load`]. Use that for full startup
    /// coordination with graceful degradation and dependency validation.
    ///
    /// [`on_error`]: OptionalSection::on_error
    /// [`requires`]: OptionalSection::requires
    fn load_optional(loader: &DefaultLoader) -> Result<FeatureState<Self>, ConfigError>
    where
        Self: Sized,
    {
        let state: FeatureState<Self> = loader.load_optional_section(Self::section_name())?;
        if let FeatureState::Enabled(ref value) = state {
            value.validate_enabled()?;
        }
        Ok(state)
    }
}