schema only.Expand description
A JSON Schema for the files this program reads.
schemars describes a struct. What an editor needs is a description of the
file, and the two are not the same thing here: a config file is a map of
sections, and a struct is one section. This module is the difference —
wrapping a struct’s schema under its section key, marking the fields
#[config(secret)] covers, and merging several structs into the one file
they share.
Opt in with the schema argument, which is what emits schema(). Like
save, it is a separate argument rather than something every type gets,
because the method needs a trait — JsonSchema — that the user has to
derive.
use schemars::JsonSchema;
use serde::Deserialize;
#[dynamic_config::dynamic_config(files = ["config.json"], key = "db", schema)]
#[derive(Deserialize, JsonSchema)]
struct DbConfig {
host: String,
#[config(secret)]
password: String,
}
let schema = DbConfig::schema();
// A file-level schema: `{"db": {...}}`, not `{...}`.
assert!(schema["properties"]["db"].is_object());
// And the secret is marked as one.
assert_eq!(
schema["properties"]["db"]["properties"]["password"]["writeOnly"],
true
);§Wiring it up
| Format | How the editor finds it |
|---|---|
| JSON | "$schema": "./config.schema.json" as a top-level key in the file |
| YAML | # yaml-language-server: $schema=./config.schema.json as the first line |
| TOML | #:schema ./config.schema.json as the first line |
The JSON case is the reason $schema is the one top-level key this crate
does not treat as a section. The other two are comments, and were never a
problem.
§What is deliberately not in the schema
Nothing is required, at any depth. schemars marks every field that is
neither Option nor #[serde(default)] as required, which 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 none of them are visible to an
editor. Left in place it would light up every 12-factor deployment’s config
file in red for values that are perfectly well supplied. check() answers
the question a schema cannot — does this actually resolve — with every
layer in view.
The top level accepts unknown keys. Other sections belong to other
structs, and one struct’s schema has no business calling them a mistake. Use
merge to describe the whole file, and unknown-key detection —
check() — for the part a schema cannot see.