1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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)
}
}