Schema
The schema method
#![allow(unused)] fn main() { let schema = DbConfig::builder("db").schema(); }
With the schema feature, the builder has schema(). It lives on the builder
rather than on the type for two reasons: the schema wraps the struct under the
builder's key, which the type alone does not know, and the method needs
T: JsonSchema — a bound a generated inherent method cannot carry (rustc
rejects an inherent method whose bound a concrete Self does not meet, at the
definition rather than at the call; the builder's generic impl block can
state it).
A schema for the config files
With the schema feature, every config type can describe the file it reads, so
an editor completes and validates it:
#![allow(unused)] fn main() { #[dynamic_config] #[derive(Deserialize, JsonSchema)] struct DbConfig { /// Where the database lives. <- becomes the hover text host: String, #[config(secret)] <- becomes `writeOnly: true` password: String, } let schema = DbConfig::builder("db").schema(); // Several types over one file describe that one file together. let whole = dynamic_config::schema::merge([ DbConfig::builder("db").schema(), ServerConfig::builder("server").schema(), ]); }
What comes out describes the file, not the struct — the struct is one section, and a config file is a map of them, so the schema is the struct's wrapped under its key.
| Format | How the editor finds it |
|---|---|
| JSON | "$schema": "./config.schema.json" as a top-level key |
| YAML | # yaml-language-server: $schema=./config.schema.json |
| TOML | #:schema ./config.schema.json |
The JSON row is why $schema is the one top-level key this crate does not read
as a section: otherwise wiring the schema into the file it describes would stop
the file from loading.
Nothing is marked required
schemars marks every field that is neither Option nor #[serde(default)] as
required. That is right for a struct and wrong for a config file: the
environment, a flag, an override or a computed default can all supply a value,
and an editor sees none of them. Left in place it would light up every 12-factor
config file in red for values that are perfectly well supplied — so the emitted
schema drops required at every depth.
The question a schema cannot answer — does this actually resolve — is what
check() is for, with
every layer in view.