Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

FormatHow 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.