#[non_exhaustive]pub struct DaemonConfig {
pub daemon: DaemonSection,
pub whistle: WhistleSection,
pub style: StyleSection,
pub interpreters: BTreeMap<String, String>,
pub dog: BTreeMap<String, Table>,
}Expand description
Parsed daemon configuration with raw per-dog sections
Dog sections stay untyped here: each dog deserializes its own
[dog.<name>] table so dog config schemas live with the dog code.
#[non_exhaustive]: this struct has grown a section per phase — style
most recently — and each one would otherwise be a breaking change for an
out-of-tree struct literal. That is IR-20’s ordinary reasoning applied to
a struct. It is not a validation gate, and this type is deliberately
not the proof token crate::config::ResolvedApp is: the attribute blocks
struct literals and functional-update syntax from outside this crate, but
not field mutation, and Self::default followed by an assignment to
daemon.max_cron_sleep reaches an unvalidated value without a literal.
The contract is therefore stated, not enforced. Self::load and
Self::load_layered are the validating constructors; a caller that
mutates a loaded config afterwards is out of contract, and shep-core does
not detect it and, with public fields, cannot.
That is the right trade here because nothing ever receives one of these.
ResolvedApp protects a property of travel — the supervisor is handed one
and must trust normalization it cannot see. Every production site loads a
DaemonConfig and consumes it within a few lines (run_daemon renders it
straight into BootOptions; shep-daemon’s dogs reads one
[dog.<name>] table; shep-cli’s whistle::gate reads one boolean), and
the daemon holds a BootOptions, not this. Guarding the one
max_cron_sleep floor against a caller who is already out of contract
would cost accessors for every field of every section, including a
BTreeMap<String, toml::Table> two crates legitimately read. If an
out-of-tree caller ever does need to mutate and re-check, the answer is to
make validate public — one line, non-breaking — not to privatise the
fields. docs/specs/deferred.md records this as resolved.
Nothing in the repository observes the attribute itself: it is invisible
inside the defining crate, and seeing it needs a trybuild compile-fail
tier this project declined once already for ProcessInfo (see
tests/process_info_builder_from_outside_the_crate.rs, which admits the
same gap and is required to stay shep-core’s only tests/ file).
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.daemon: DaemonSectionThe [daemon] section
whistle: WhistleSectionThe [whistle] section
style: StyleSectionThe [style] section
interpreters: BTreeMap<String, String>The [interpreters] section: a script extension (no leading dot,
"js" not ".js") mapped to the interpreter that runs it.
Read by the CLI only, and applied before a request ever reaches the
wire: shep start’s target resolution folds a match into an app’s
own AppConfig::interpreter
exactly when that field is still unset, the same way --cwd and
--fold are folded in there. An app that already names an
interpreter – explicitly in a Flockfile, including the literal
"none" – is never touched by this map, and --interpreter on the
command line outranks both. The daemon itself never reads this
field; by the time an app reaches it, the CLI has already resolved
the question.
Lives in this daemon-config grammar for the reason
StyleSection’s own doc gives for itself: RawDaemonConfig
denies unknown top-level keys, so a section this type does not know
about is a hard parse error for every shep.toml carrying one –
registering it here is what keeps [interpreters] from breaking
every daemon boot the moment an operator, or shep’s own first-run
scaffold, writes one.
dog: BTreeMap<String, Table>Raw [dog.<name>] sections keyed by dog name
Implementations§
Source§impl DaemonConfig
impl DaemonConfig
Sourcepub fn load(
file_source: Option<&str>,
env: &dyn Fn(&str) -> Option<String>,
) -> Result<Self, DaemonConfigError>
pub fn load( file_source: Option<&str>, env: &dyn Fn(&str) -> Option<String>, ) -> Result<Self, DaemonConfigError>
Builds config from optional file source + environment overrides
file < env, validated. Equivalent to
Self::load_layered with an empty DaemonOverrides; unchanged
for every existing caller.
§Errors
DaemonConfigError::Toml— the file source is invalid TOML.DaemonConfigError::BadEnvValue— aSHEP_*value is not parseable (SHEP_LOG_JSONaccepts1|0|true|false;SHEP_LOG_LEVELaccepts aLogLevelname;SHEP_MAX_CRON_SLEEPparses as anUpDuration).DaemonConfigError::BelowMinimum— the effectivemax_cron_sleep(file,SHEP_MAX_CRON_SLEEP, whichever won) is below the floor.
Sourcepub fn load_layered(
file_source: Option<&str>,
env: &dyn Fn(&str) -> Option<String>,
overrides: &DaemonOverrides,
) -> Result<Self, DaemonConfigError>
pub fn load_layered( file_source: Option<&str>, env: &dyn Fn(&str) -> Option<String>, overrides: &DaemonOverrides, ) -> Result<Self, DaemonConfigError>
Builds config from optional file source + environment + CLI-flag overrides
file < env < flags (spec §5), validated exactly once, at the end —
see the private validate method below for why validating per layer
instead would be wrong.
§Errors
DaemonConfigError::Toml— the file source is invalid TOML.DaemonConfigError::BadEnvValue— aSHEP_*value is not parseable (SHEP_LOG_JSONaccepts1|0|true|false;SHEP_LOG_LEVELaccepts aLogLevelname;SHEP_MAX_CRON_SLEEPparses as anUpDuration).DaemonConfigError::BelowMinimum— the effectivemax_cron_sleep(file,SHEP_MAX_CRON_SLEEP, or--max-cron-sleep, whichever won) is below the floor.
Trait Implementations§
Source§impl Clone for DaemonConfig
impl Clone for DaemonConfig
Source§fn clone(&self) -> DaemonConfig
fn clone(&self) -> DaemonConfig
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for DaemonConfig
Debug implementation does not leak dog config values (IR-41)
impl Debug for DaemonConfig
Debug implementation does not leak dog config values (IR-41)