rust-config-tree Handbuch
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
Dies ist das deutsche Handbuch fuer rust-config-tree.
Beginne mit Einfuehrung, Schnellstart oder den ausfuehrbaren Beispielen.
Einfuehrung
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
rust-config-tree stellt wiederverwendbares Laden von Konfigurationsbaeumen
und CLI-Helfer fuer Rust-Anwendungen bereit, die geschichtete
Konfigurationsdateien verwenden.
Die Crate ist um eine kleine Verantwortungsaufteilung herum gebaut:
confiquebesitzt Schemadefinitionen, Code-Defaults, Validierung und Konfigurationsvorlagenerzeugung.figmentbesitzt Laufzeitladen und Laufzeit-Quellenmetadaten.rust-config-treebesitzt rekursive Include-Traversierung, Include-Pfadaufloesung,.env-Laden, Vorlagenziel-Erkennung und wiederverwendbare clap-Befehle.
Die Crate ist nuetzlich, wenn eine Anwendung ein natuerliches Dateilayout wie dieses verwenden soll:
include:
- config/server.yaml
- config/database.yaml
log:
level: info
Jede inkludierte Datei kann dieselbe Schemaform verwenden, und relative
Include-Pfade werden von der Datei aufgeloest, die sie deklariert hat. Die
finale Konfiguration bleibt ein normales confique-Schema.
Hauptfunktionen
- Rekursive Include-Traversierung mit Zyklenerkennung.
- Relative Include-Pfade werden von der deklarierenden Datei aufgeloest.
.envwird geladen, bevor Umgebungsprovider ausgewertet werden.- Schema-deklarierte Umgebungsvariablen ohne Trennzeichen-Splitting.
- Figment-Metadaten fuer Quellenverfolgung zur Laufzeit.
- Quellenverfolgungsereignisse auf TRACE-Ebene ueber
tracing. - Erzeugung von Draft-7-JSON-Schemas fuer Editor-Vervollstaendigung und grundlegende Schema-Pruefungen.
- Feldwertvalidierung im Anwendungscode mit
#[config(validate = Self::validate)], ausgefuehrt durchload_configodervalidate-config. - Vorlagenerzeugung fuer YAML, TOML, JSON und JSON5.
- TOML-
#:schema, YAML-Language-Server-Modelines und JSON/JSON5-$schema- Felder fuer erzeugte Vorlagen. - Opt-in-YAML-Vorlagenaufteilung fuer mit
x-tree-splitmarkierte Abschnitte. - Eingebaute clap-Unterbefehle fuer Konfigurationsvorlagen, JSON-Schema und Shell-Vervollstaendigungen.
- Eine untergeordnete Tree-API fuer Aufrufer, die
confiquenicht verwenden.
Oeffentliche Einstiegspunkte
Diese APIs eignen sich fuer die meisten Anwendungen:
load_config::<S>(path)laedt das finale Schema.load_config_with_figment::<S>(path)laedt das Schema und gibt den Figment-Graphen fuer Quellenverfolgung zurueck.write_config_templates::<S>(config_path, output_path)schreibt die Root-Vorlage und rekursiv entdeckte Kind-Vorlagen.write_config_schemas::<S>(output_path)schreibt Draft-7-JSON-Schemas fuer Root und Abschnitte.handle_config_command::<Cli, S>(command, config_path)behandelt eingebaute clap-Konfigurationsbefehle.
Verwende load_config_tree, wenn du das Traversierungsprimitiv ohne
confique brauchst.
Schnellstart
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
Fuege die Crate und die Schema-/Laufzeitbibliotheken hinzu, die deine Anwendung verwendet:
[dependencies]
rust-config-tree = "0.2"
confique = { version = "0.4", features = ["yaml", "toml", "json5"] }
figment = { version = "0.10", features = ["yaml", "toml", "json", "env"] }
schemars = { version = "1", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
clap = { version = "4", features = ["derive"] }
Definiere ein confique-Schema und implementiere ConfigSchema fuer den
Root-Typ:
#![allow(unused)]
fn main() {
use std::path::PathBuf;
use confique::Config;
use rust_config_tree::ConfigSchema;
#[derive(Debug, Config)]
struct AppConfig {
#[config(default = [])]
include: Vec<PathBuf>,
#[config(nested)]
server: ServerConfig,
}
#[derive(Debug, Config)]
struct ServerConfig {
#[config(default = "127.0.0.1")]
#[config(env = "APP_SERVER_BIND")]
bind: String,
#[config(default = 8080)]
#[config(env = "APP_SERVER_PORT")]
port: u16,
}
impl ConfigSchema for AppConfig {
fn include_paths(layer: &<Self as Config>::Layer) -> Vec<PathBuf> {
layer.include.clone().unwrap_or_default()
}
}
}
Lade die Konfiguration:
#![allow(unused)]
fn main() {
use rust_config_tree::load_config;
let config = load_config::<AppConfig>("config.yaml")?;
println!("{config:#?}");
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
}
Verwende eine Root-Datei mit rekursiven Includes:
# config.yaml
include:
- config/server.yaml
# config/server.yaml
server:
bind: 0.0.0.0
port: 3000
Die Standardprioritaet von load_config ist:
environment variables
> config files, with later merged files overriding earlier files
> confique code defaults
Wenn Includes ueber die High-Level-API geladen werden, hat die Root-Datei die hoechste Dateiprioritaet. Inkludierte Dateien liefern Werte mit niedrigerer Prioritaet und koennen fuer Defaults oder abschnittsspezifische Dateien genutzt werden.
Kommandozeilenargumente sind anwendungsspezifisch, daher liest load_config
sie nicht automatisch. Verwende die ConfigOverrides-Ableitungsmakro, um
aus geparsten CLI-Flags einen Ueberschreibungsanbieter zu erstellen:
#![allow(unused)]
fn main() {
use clap::Parser;
use rust_config_tree::{
ConfigSchema,
cli::ConfigOverrides,
config::{build_config_figment, load_config_from_figment},
};
#[derive(Debug, Parser, ConfigOverrides)]
struct Cli {
#[arg(long)]
config: Option<std::path::PathBuf>,
#[arg(long)]
#[config_override(path = "server.port")]
server_port: Option<u16>,
#[arg(long)]
#[config_override(path = "log.level")]
log_level: Option<String>,
}
let cli = Cli::parse();
let figment = build_config_figment::<AppConfig>("config.yaml")?
.merge(cli.config_overrides()?);
let config = load_config_from_figment::<AppConfig>(&figment)?;
let _ = config;
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
}
Das #[config_override(path = "...")]-Attribut bildet jedes CLI-Flag auf
einen gepunkteten Konfigurationspfad ab. Nur angegebene Flags erzeugen
Ueberschreibungswerte; weggelassene Flags verschwinden. Der
Ueberschreibungsanbieter wird zuletzt gemerged, sodass angegebene Flags Datei-
und Umgebungsvariablen ueberschreiben:
command-line overrides
> environment variables
> config files
> confique code defaults
Konfigurationsschema
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
Anwendungsschemas sind normale confique-Konfigurationstypen. Das Root-Schema
muss ConfigSchema implementieren, damit rust-config-tree rekursive Includes
aus der zwischengeschalteten confique-Schicht entdecken kann.
#![allow(unused)]
fn main() {
use std::path::PathBuf;
use confique::Config;
use schemars::JsonSchema;
use rust_config_tree::ConfigSchema;
#[derive(Debug, Config, JsonSchema)]
struct AppConfig {
#[config(default = [])]
include: Vec<PathBuf>,
#[config(nested)]
#[schemars(extend("x-tree-split" = true))]
database: DatabaseConfig,
}
#[derive(Debug, Config, JsonSchema)]
struct DatabaseConfig {
#[config(env = "APP_DATABASE_URL")]
url: String,
#[config(default = 16)]
#[config(env = "APP_DATABASE_POOL_SIZE")]
pool_size: u32,
}
impl ConfigSchema for AppConfig {
fn include_paths(layer: &<Self as Config>::Layer) -> Vec<PathBuf> {
layer.include.clone().unwrap_or_default()
}
}
}
Include-Feld
Das Include-Feld kann beliebig heissen. rust-config-tree kennt es nur ueber
ConfigSchema::include_paths.
Das Feld sollte normalerweise einen leeren Default haben:
#![allow(unused)]
fn main() {
#[config(default = [])]
include: Vec<PathBuf>,
}
Der Loader erhaelt fuer jede Datei eine teilweise geladene Schicht. Dadurch kann er Kind-Konfigurationsdateien entdecken, bevor das finale Schema zusammengefuehrt und validiert wird.
Verschachtelte Abschnitte
Verwende #[config(nested)] fuer strukturierte Abschnitte. Verschachtelte
Abschnitte werden immer fuer das Laden zur Laufzeit genutzt. Fuege
#[schemars(extend("x-tree-split" = true))] hinzu, wenn ein nested Feld
zusaetzlich als eigenes *.yaml-Template und <section>.schema.json-Schema
erzeugt werden soll:
#![allow(unused)]
fn main() {
#[derive(Debug, Config, JsonSchema)]
struct AppConfig {
#[config(nested)]
#[schemars(extend("x-tree-split" = true))]
server: ServerConfig,
}
}
Die natuerliche YAML-Form ist:
server:
bind: 127.0.0.1
port: 8080
Nur-Umgebung-Felder
Markiere ein Blattfeld mit #[schemars(extend("x-env-only" = true))], wenn sein Wert nur aus einer Umgebungsvariable kommen soll und nicht in generierten Konfigurationsdateien erscheinen darf. Generierte YAML-Vorlagen und JSON-Schemas lassen env-only-Felder weg, und dadurch leere Elternobjekte werden entfernt.
#![allow(unused)]
fn main() {
#[config(env = "APP_SECRET")]
#[schemars(extend("x-env-only" = true))]
secret: String,
}
Feldwertvalidierung
Erzeugte *.schema.json-Dateien sind nur fuer IDE-Vervollstaendigung und
grundlegende Editor-Pruefungen gedacht. Sie entscheiden nicht, ob ein konkreter
Feldwert fuer die Anwendung gueltig ist.
Feldwertvalidierung muss im Code mit #[config(validate = Self::validate)]
implementiert werden. Der Validator laeuft, wenn die finale Konfiguration ueber
load_config geladen oder ueber validate-config geprueft wird.
Abschnittspfade fuer Vorlagen ueberschreiben
Wenn eine Vorlagenquelle keine Includes hat, kann die Crate Kind-Vorlagendateien
aus mit x-tree-split markierten verschachtelten Schemaabschnitten ableiten. Der Standardpfad auf oberster
Ebene ist <section>.yaml.
Ueberschreibe diesen Pfad mit template_path_for_section:
#![allow(unused)]
fn main() {
impl ConfigSchema for AppConfig {
fn include_paths(layer: &<Self as Config>::Layer) -> Vec<PathBuf> {
layer.include.clone().unwrap_or_default()
}
fn template_path_for_section(section_path: &[&str]) -> Option<PathBuf> {
match section_path {
["database"] => Some(PathBuf::from("examples/database.yaml")),
_ => None,
}
}
}
}
Laden zur Laufzeit
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
Das Laden zur Laufzeit ist bewusst zwischen Figment und confique aufgeteilt:
figment:
runtime file loading
runtime environment loading
runtime source metadata
confique:
schema metadata
defaults
validation
config templates
Die Haupt-API ist:
#![allow(unused)]
fn main() {
use rust_config_tree::load_config;
let config = load_config::<AppConfig>("config.yaml")?;
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
}
Verwende load_config_with_figment, wenn die Anwendung Quellenmetadaten
braucht:
#![allow(unused)]
fn main() {
use rust_config_tree::load_config_with_figment;
let (config, figment) = load_config_with_figment::<AppConfig>("config.yaml")?;
let _ = (config, figment);
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
}
Ladeschritte
Der High-Level-Loader fuehrt diese Schritte aus:
- Root-Konfigurationspfad lexikalisch aufloesen.
- Die erste
.env-Datei laden, die beim Aufwaertslaufen ab dem Root-Konfigurationsverzeichnis gefunden wird. - Jede Konfigurationsdatei als Teilschicht laden, um Includes zu entdecken.
- Einen Figment-Graphen aus den entdeckten Konfigurationsdateien bauen.
- Den
ConfiqueEnvProvidermit hoeherer Prioritaet als Dateien zusammenfuehren. - Optional anwendungsspezifische CLI-Ueberschreibungen zusammenfuehren.
- Eine
confique-Schicht aus Figment extrahieren. confique-Code-Defaults anwenden.- Das finale Schema validieren und konstruieren.
load_config und load_config_with_figment fuehren die Schritte 1-5 und 7-9
aus. Schritt 6 ist anwendungsspezifisch, weil diese Crate nicht ableiten kann,
wie ein CLI-Flag auf ein Schemafeld abgebildet wird.
Dateiformate
Der Laufzeit-Dateiprovider wird aus der Erweiterung des Konfigurationspfads gewaehlt:
.yamlund.ymlverwenden YAML..tomlverwendet TOML..jsonund.json5verwenden JSON.- unbekannte oder fehlende Erweiterungen verwenden YAML.
Die Vorlagenerzeugung verwendet weiterhin die Template-Renderer von confique fuer YAML, TOML und JSON5-kompatible Ausgabe.
Include-Prioritaet
Der High-Level-Loader fuehrt Dateiprovider so zusammen, dass inkludierte Dateien niedrigere Prioritaet haben als die Datei, die sie inkludiert. Die Root- Konfigurationsdatei hat die hoechste Dateiprioritaet.
Umgebungsvariablen haben hoehere Prioritaet als alle Konfigurationsdateien.
confique-Defaults werden nur fuer Werte verwendet, die nicht von
Laufzeitprovidern geliefert werden.
Wenn CLI-Ueberschreibungen nach build_config_figment zusammengefuehrt werden,
gilt diese vollstaendige Prioritaet:
command-line overrides
> environment variables
> config files
> confique code defaults
Die Kommandozeilensyntax wird nicht von rust-config-tree definiert. Ein Flag
wie --server-port kann server.port ueberschreiben, wenn die Anwendung den
geparsten Wert in einen verschachtelten serialisierten Provider abbildet. Eine
Syntax mit Punkten wie --server.port oder a.b.c existiert nur, wenn die
Anwendung sie implementiert.
Das bedeutet: CLI-Prioritaet gilt nur fuer Schluessel, die im Ueberschreibungsprovider der Anwendung vorhanden sind. Nutze sie fuer operative Werte, die haeufig fuer einen einzelnen Lauf geaendert werden. Dauerhafte Konfiguration bleibt in Dateien.
#![allow(unused)]
fn main() {
use figment::providers::Serialized;
use serde::Serialize;
use rust_config_tree::{build_config_figment, load_config_from_figment};
#[derive(Debug, Serialize)]
struct CliOverrides {
#[serde(skip_serializing_if = "Option::is_none")]
server: Option<CliServerOverrides>,
}
#[derive(Debug, Serialize)]
struct CliServerOverrides {
#[serde(skip_serializing_if = "Option::is_none")]
port: Option<u16>,
}
let cli_overrides = CliOverrides {
server: Some(CliServerOverrides { port: Some(9000) }),
};
let figment = build_config_figment::<AppConfig>("config.yaml")?
.merge(Serialized::defaults(cli_overrides));
let config = load_config_from_figment::<AppConfig>(&figment)?;
let _ = config;
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
}
Umgebungsvariablen
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
Umgebungsvariablennamen werden im Schema mit confique deklariert:
#![allow(unused)]
fn main() {
#[derive(Debug, Config)]
struct DatabaseConfig {
#[config(env = "APP_DATABASE_URL")]
url: String,
#[config(default = 16)]
#[config(env = "APP_DATABASE_POOL_SIZE")]
pool_size: u32,
}
}
rust-config-tree liest diese Namen aus confique::Config::META und baut
einen Figment-Provider, der jede Umgebungsvariable ihrem exakten Feldpfad
zuordnet.
Verwende fuer diese Crate kein trennzeichenbasiertes Figment-Environment- Mapping:
#![allow(unused)]
fn main() {
// Do not use this pattern for rust-config-tree schemas.
Env::prefixed("APP_").split("_")
Env::prefixed("APP_").split("__")
}
split("_") behandelt Unterstriche als Trenner fuer verschachtelte Schluessel.
Dadurch wird aus APP_DATABASE_POOL_SIZE ein Pfad wie database.pool.size,
was mit Rust-Feldnamen wie pool_size kollidiert.
Mit ConfiqueEnvProvider ist die Zuordnung explizit:
APP_DATABASE_POOL_SIZE -> database.pool_size
Einzelne Unterstriche bleiben Teil des Umgebungsvariablennamens. Figment raet die Verschachtelungsregel nicht.
Dotenv-Laden
Bevor Laufzeitprovider ausgewertet werden, sucht der Loader nach einer
.env-Datei, indem er vom Verzeichnis der Root-Konfigurationsdatei nach oben
laeuft.
Bereits vorhandene Prozess-Umgebungsvariablen bleiben erhalten. Werte aus
.env fuellen nur fehlende Umgebungsvariablen.
Beispiel:
APP_SERVER_PORT=9000
APP_DATABASE_POOL_SIZE=64
Diese Variablen ueberschreiben Konfigurationsdateiwerte, wenn das Schema
passende #[config(env = "...")]-Attribute deklariert.
Werte parsen
Der Bridge-Provider laesst Figment Umgebungswerte parsen. Er ruft nicht die
parse_env-Hooks von confique auf. Lege komplexe Werte in
Konfigurationsdateien ab, ausser die Figment-Syntax fuer Umgebungswerte passt
gut zum Typ.
Quellenverfolgung
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
Verwende load_config_with_figment, um den Figment-Graphen zu behalten, der
beim Laufzeitladen verwendet wurde:
#![allow(unused)]
fn main() {
use rust_config_tree::load_config_with_figment;
let (config, figment) = load_config_with_figment::<AppConfig>("config.yaml")?;
let _ = config;
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
}
Der zurueckgegebene Figment-Wert kann Quellenfragen fuer Laufzeitwerte beantworten:
#![allow(unused)]
fn main() {
if let Some(metadata) = figment.find_metadata("database.pool_size") {
let source = metadata.interpolate(
&figment::Profile::Default,
&["database", "pool_size"],
);
println!("database.pool_size came from {source}");
}
}
Fuer Werte, die von ConfiqueEnvProvider geliefert werden, gibt die
Interpolation den nativen Umgebungsvariablennamen zurueck, der im Schema
deklariert ist:
database.pool_size came from APP_DATABASE_POOL_SIZE
TRACE-Ereignisse
Der Loader gibt Quellenverfolgungsereignisse mit tracing::trace! aus. Das
geschieht nur, wenn TRACE aktiviert ist:
#![allow(unused)]
fn main() {
use rust_config_tree::{load_config_with_figment, trace_config_sources};
let (config, figment) = load_config_with_figment::<AppConfig>("config.yaml")?;
// If the tracing subscriber is initialized after config loading, emit the
// same source events again after installing the subscriber.
trace_config_sources::<AppConfig>(&figment);
let _ = config;
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
}
Jedes Ereignis verwendet das Ziel rust_config_tree::config und enthaelt:
config_key: den gepunkteten Konfigurationsschluessel.source: die gerenderte Quellenmetadatenangabe.
Werte, die nur aus confique-Defaults stammen, haben keine
Figment-Laufzeitmetadaten. Sie werden als
confique default or unset optional field gemeldet.
Vorlagenerzeugung
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
Vorlagen werden aus demselben confique-Schema erzeugt, das zur Laufzeit
verwendet wird. confique rendert den eigentlichen Vorlageninhalt,
einschliesslich Doc-Kommentaren, Defaults, Pflichtfeldern und deklarierten
Umgebungsvariablennamen.
Verwende write_config_templates:
#![allow(unused)]
fn main() {
use rust_config_tree::write_config_templates;
write_config_templates::<AppConfig>("config.yaml", "config.example.yaml")?;
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
}
Erzeuge Draft-7-JSON-Schemas fuer die Root-Konfiguration und explizit aufgeteilte verschachtelte Abschnitte:
#![allow(unused)]
fn main() {
use rust_config_tree::write_config_schemas;
write_config_schemas::<AppConfig>("schemas/myapp.schema.json")?;
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
}
Markiere ein verschachteltes Feld mit
#[schemars(extend("x-tree-split" = true))], wenn es als eigene
*.yaml-Vorlage und als eigenes <section>.schema.json-Schema erzeugt werden
soll. Nicht markierte verschachtelte Felder bleiben in der Elternvorlage und im
Elternschema.
Markiere ein Blattfeld mit #[schemars(extend("x-env-only" = true))], wenn der
Wert nur aus Umgebungsvariablen kommen darf. Generierte Vorlagen und
JSON-Schemas lassen env-only-Felder weg, und dadurch leere Elternobjekte werden
entfernt.
Erzeugte Schemas lassen required-Einschraenkungen weg. IDEs koennen weiterhin
Vervollstaendigung anbieten, aber partielle Dateien wie log.yaml
melden keine fehlenden Root-Felder. Das Root-Schema vervollstaendigt nur
Felder, die in die Root-Datei gehoeren; aufgeteilte Abschnittsfelder werden
dort weggelassen und durch ihre eigenen Abschnittsschemas vervollstaendigt.
Vorhandene Felder koennen weiterhin grundlegende Editor-Pruefungen erhalten,
etwa Typ-, Enum- und Unbekannte-Eigenschaft-Pruefungen, soweit sie vom erzeugten
Schema unterstuetzt werden. Erzeugte *.schema.json-Dateien entscheiden nicht,
ob ein konkreter Feldwert fuer die Anwendung gueltig ist. Feldwertvalidierung
muss im Code mit #[config(validate = Self::validate)] implementiert werden;
load_config und validate-config fuehren diese Laufzeitvalidierung aus.
Binde diese Schemas aus erzeugten TOML-, YAML-, JSON- und JSON5-Vorlagen:
#![allow(unused)]
fn main() {
use rust_config_tree::write_config_templates_with_schema;
write_config_templates_with_schema::<AppConfig>(
"config.toml",
"config.example.toml",
"schemas/myapp.schema.json",
)?;
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
}
TOML- und YAML-Root-Vorlagen binden das Root-Schema und vervollstaendigen keine
aufgeteilten untergeordneten Abschnittsfelder. Aufgeteilte
YAML-Abschnittsvorlagen binden ihr Abschnittsschema. JSON- und JSON5-Vorlagen
erhalten ein oberstes $schema-Feld, das VS Code erkennen kann. VS Code
json.schemas bleibt als alternative Bindung moeglich.
Das Ausgabeformat wird aus dem Ausgabepfad abgeleitet:
.yamlund.ymlerzeugen YAML..tomlerzeugt TOML..jsonund.json5erzeugen JSON5-kompatible Vorlagen.- unbekannte oder fehlende Erweiterungen erzeugen YAML.
Schema-Bindungen
Mit einem Schemapfad schemas/myapp.schema.json verwenden erzeugte
Root-Vorlagen:
#:schema ./schemas/myapp.schema.json
# yaml-language-server: $schema=./schemas/myapp.schema.json
Erzeugte Abschnittsvorlagen binden Abschnittsschemas:
# log.yaml
# yaml-language-server: $schema=./schemas/log.schema.json
Erzeugte JSON- und JSON5-Vorlagen schreiben ein oberstes $schema-Feld, das
VS Code erkennt. Editor-Einstellungen bleiben optional:
{
"json.schemas": [
{
"fileMatch": [
"/config.json",
"/config.*.json"
],
"url": "./schemas/myapp.schema.json"
}
]
}
Auswahl der Vorlagenquelle
Die Vorlagenerzeugung waehlt ihren Quellbaum in dieser Reihenfolge:
- Vorhandener Konfigurationspfad.
- Vorhandener Ausgabe-Vorlagenpfad.
- Ausgabepfad, behandelt als neuer leerer Vorlagenbaum.
So kann ein Projekt Vorlagen aus aktuellen Konfigurationsdateien aktualisieren, ein vorhandenes Vorlagenset aktualisieren oder ein neues Vorlagenset nur aus dem Schema erzeugen.
Gespiegelte Include-Baeume
Wenn die Quelldatei Includes deklariert, spiegeln erzeugte Vorlagen diese Include-Pfade unter dem Ausgabeverzeichnis.
# config.yaml
include:
- server.yaml
Das Erzeugen von config.example.yaml schreibt:
config.example.yaml
server.yaml
Relative Include-Ziele werden unter dem Elternverzeichnis der Ausgabedatei gespiegelt. Absolute Include-Ziele bleiben absolut.
Opt-in-Abschnittsaufteilung
Wenn eine Quelldatei keine Includes hat, kann die Crate Include-Ziele aus
mit x-tree-split markierten verschachtelten Schemaabschnitten ableiten. Fuer ein Schema mit einem markierten Abschnitt
server kann eine leere Root-Vorlagenquelle Folgendes erzeugen:
config.example.yaml
server.yaml
Die Root-Vorlage erhaelt einen Include-Block, und server.yaml enthaelt
nur den Abschnitt server. Verschachtelte Abschnitte werden nur rekursiv aufgeteilt, wenn diese Felder ebenfalls x-tree-split tragen.
IDE-Vervollstaendigung
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
Erzeugte JSON-Schemas koennen von TOML-, YAML-, JSON- und JSON5-
Konfigurationsdateien verwendet werden. Sie werden aus demselben Rust-Typ
erzeugt, den confique verwendet:
#![allow(unused)]
fn main() {
use confique::Config;
use schemars::JsonSchema;
#[derive(Debug, Config, JsonSchema)]
struct AppConfig {
#[config(nested)]
#[schemars(extend("x-tree-split" = true))]
server: ServerConfig,
}
}
Erzeuge sie mit:
#![allow(unused)]
fn main() {
use rust_config_tree::write_config_schemas;
write_config_schemas::<AppConfig>("schemas/myapp.schema.json")?;
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
}
Dies schreibt das Root-Schema und Abschnittsschemas wie
schemas/server.schema.json. Erzeugte Schemas lassen required-
Einschraenkungen weg, damit Vervollstaendigung fuer partielle
Konfigurationsdateien ohne Fehlende-Felder-Diagnosen funktioniert. Das
Root-Schema laesst aufgeteilte Abschnittseigenschaften weg, sodass
Kindabschnitts-Vervollstaendigung nur in Dateien verfuegbar ist, die das
passende Abschnittsschema binden. Nicht markierte verschachtelte Abschnitte
bleiben im Root-Schema.
Mit x-env-only markierte Felder werden aus erzeugten Schemas weggelassen, sodass IDEs keine Secrets oder andere Werte vorschlagen, die nur aus Umgebungsvariablen kommen duerfen.
IDE-Schemas dienen der Vervollstaendigung und grundlegenden Editor-Pruefungen,
etwa Typ-, Enum- und Unbekannte-Eigenschaft-Pruefungen, soweit sie vom erzeugten
Schema unterstuetzt werden. Sie entscheiden nicht, ob ein konkreter Feldwert fuer
die Anwendung gueltig ist. Feldwertvalidierung muss im Code mit
#[config(validate = Self::validate)] implementiert und dann ueber
load_config oder validate-config ausgefuehrt werden. Pflichtfelder und die
finale Validierung der zusammengefuehrten Konfiguration verwenden ebenfalls
diese Laufzeitpfade.
TOML
TOML-Dateien sollten das Schema mit einer #:schema-Direktive am Dateianfang
binden:
#:schema ./schemas/myapp.schema.json
[server]
bind = "0.0.0.0"
port = 3000
Verwende in TOML kein Root-Feld $schema = "...". Es wird zu echten
Konfigurationsdaten und kann die Laufzeit-Deserialisierung beeinflussen.
write_config_templates_with_schema fuegt die #:schema-Direktive fuer
TOML-Vorlagen automatisch hinzu.
YAML
YAML-Dateien sollten die YAML-Language-Server-Modeline verwenden:
# yaml-language-server: $schema=./schemas/myapp.schema.json
server:
bind: 0.0.0.0
port: 3000
write_config_templates_with_schema fuegt diese Modeline fuer YAML-Vorlagen
automatisch hinzu. Aufgeteilte YAML-Vorlagen binden ihr Abschnittsschema, zum
Beispiel bindet log.yaml ./schemas/log.schema.json.
JSON
JSON- und JSON5-Dateien koennen ein Schema mit einem obersten $schema-Feld binden. write_config_templates_with_schema fuegt es fuer erzeugte JSON- und JSON5-Vorlagen automatisch hinzu:
{
"$schema": "./schemas/myapp.schema.json"
}
Editor-Einstellungen bleiben nuetzlich, wenn ein Projekt keine Bindung in der Datei will:
{
"json.schemas": [
{
"fileMatch": [
"/config.json",
"/config.*.json",
"/deploy/*.json"
],
"url": "./schemas/myapp.schema.json"
}
]
}
YAML kann ebenfalls ueber VS-Code-Einstellungen gebunden werden:
{
"yaml.schemas": {
"./schemas/myapp.schema.json": [
"config.yaml",
"config.*.yaml",
"deploy/*.yaml"
]
}
}
Das finale Layout ist:
schemas/myapp.schema.json:
Nur Felder der Root-Datei
schemas/server.schema.json:
Schema fuer den Abschnitt server
config.toml:
#:schema ./schemas/myapp.schema.json
config.yaml:
# yaml-language-server: $schema=./schemas/myapp.schema.json
server.yaml:
# yaml-language-server: $schema=./schemas/server.schema.json
config.json:
"$schema": "./schemas/myapp.schema.json"
Referenzen:
CLI-Integration
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
ConfigCommand stellt wiederverwendbare clap-Unterbefehle bereit:
generate-templategenerate-schemavalidate-configcompletionsinstall-completionsuninstall-completions
Diese eingebauten Unterbefehle sind von anwendungsspezifischen Flags fuer Konfigurationsueberschreibungen getrennt. Fuehre solche Flags im Laufzeit-Ladepfad als Figment-Provider zusammen.
Konfigurations-Ueberschreibungsflags bleiben Teil der CLI der konsumierenden
Anwendung. Ihre Namen muessen nicht mit gepunkteten Konfigurationspfaden
uebereinstimmen. Zum Beispiel kann die Anwendung --server-port parsen und auf
den verschachtelten Konfigurationsschluessel server.port abbilden. Nur Flags,
die die Anwendung in CliOverrides abbildet, beeinflussen Konfigurationswerte.
Fuege es flach in ein Befehls-Enum der Anwendung ein:
- Behalte den eigenen
Parser-Typ der Anwendung. - Behalte das eigene
Subcommand-Enum der Anwendung. - Fuege
#[command(flatten)] Config(ConfigCommand)zu diesem Enum hinzu. - Clap erweitert die flachen
ConfigCommand-Varianten auf dieselbe Befehlsebene wie die eigenen Varianten der Anwendung. - Verarbeite die Variante
Config(command)und uebergib sie anhandle_config_command.
use std::path::PathBuf;
use clap::{Parser, Subcommand};
use confique::Config;
use schemars::JsonSchema;
use rust_config_tree::{ConfigCommand, ConfigSchema, handle_config_command, load_config};
#[derive(Debug, Config, JsonSchema)]
struct AppConfig {
#[config(default = [])]
include: Vec<PathBuf>,
}
impl ConfigSchema for AppConfig {
fn include_paths(layer: &<Self as Config>::Layer) -> Vec<PathBuf> {
layer.include.clone().unwrap_or_default()
}
}
#[derive(Debug, Parser)]
#[command(name = "demo")]
struct Cli {
#[arg(long, default_value = "config.yaml")]
config: PathBuf,
#[command(subcommand)]
command: Command,
}
#[derive(Debug, Subcommand)]
enum Command {
Run,
#[command(flatten)]
Config(ConfigCommand),
}
fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let cli = Cli::parse();
match cli.command {
Command::Run => {
let config = load_config::<AppConfig>(&cli.config)?;
println!("{config:#?}");
}
Command::Config(command) => {
handle_config_command::<Cli, AppConfig>(command, &cli.config)?;
}
}
Ok(())
}
Konfigurationsvorlagen
demo generate-template
Der Befehl schreibt Vorlagen in den von --output angegebenen Ausgabepfad.
Wenn kein --output angegeben wird, schreibt der Befehl
config/<root_config_name>/<root_config_name>.example.yaml. Fuege
--schema schemas/myapp.schema.json hinzu, um erzeugte TOML-, YAML-, JSON- und
JSON5-Vorlagen an erzeugte JSON-Schemas zu binden. Aufgeteilte YAML-Vorlagen
binden das passende Abschnittsschema. JSON- und JSON5-Vorlagen erhalten ein von
VS Code erkennbares $schema-Feld. Der Befehl schreibt ausserdem Root- und
Abschnittsschemas an den gewaehlten Schemapfad.
demo generate-template --output app_config.example.toml --schema schemas/myapp.schema.json
Root- und Abschnitts-JSON-Schemas erzeugen:
demo generate-schema
Ohne --output schreibt generate-schema das Root-Schema nach
config/<root_config_name>/<root_config_name>.schema.json.
Den vollstaendigen Laufzeit-Konfigurationsbaum validieren:
demo validate-config
Erzeugte Editor-Schemas vermeiden bewusst Pflichtfeld-Diagnosen fuer
aufgeteilte Dateien. validate-config laedt Includes, wendet Defaults an und
fuehrt die finale confique-Validierung aus, einschliesslich Validatoren aus
#[config(validate = Self::validate)]. Erzeugte *.schema.json-Dateien bleiben
fuer IDE-Vervollstaendigung und grundlegende Editor-Pruefungen gedacht, nicht
fuer Feldwertlegalitaet. Bei erfolgreicher Validierung gibt es
Configuration is ok aus.
Shell-Vervollstaendigungen
Vervollstaendigungen nach stdout ausgeben:
demo completions zsh
Vervollstaendigungen installieren:
demo install-completions zsh
Vervollstaendigungen deinstallieren:
demo uninstall-completions zsh
Der Installer unterstuetzt Bash, Elvish, Fish, PowerShell und Zsh. Er schreibt die Vervollstaendigungsdatei unter das Home-Verzeichnis des Benutzers und aktualisiert die Shell-Startdatei fuer Shells, die dies benoetigen.
Bevor eine vorhandene Shell-Startdatei wie ~/.zshrc, ~/.bashrc, eine
Elvish-rc-Datei oder ein PowerShell-Profil geaendert wird, schreibt der Befehl
ein Backup neben die Originaldatei:
<rc-file>.backup.by.<program-name>.<timestamp>
Beispiele
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
Das Repository enthaelt ausfuehrbare Beispiele fuer das Laden von Konfigurationsbaeumen, CLI-Ueberschreibungen, eingebaute Konfigurationsbefehle, Vorlagenerzeugung und die untergeordnete Tree-API.
Lies den Beispielindex des Repositorys:
Fuehre Beispiele aus dem Repository-Root aus:
cargo run --example basic_loading
cargo run --example cli_overrides -- --server-port 9000
cargo run --example config_commands -- generate-template
cargo run --example config_commands -- generate-schema
cargo run --example config_commands -- validate-config
cargo run --example generate_templates
cargo run --example tree_api
Tree-API
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
Verwende die untergeordnete Tree-API, wenn die Anwendung confique nicht
verwendet oder direkten Zugriff auf Traversierungsergebnisse braucht.
#![allow(unused)]
fn main() {
use std::{
fs,
io,
path::{Path, PathBuf},
};
use rust_config_tree::{ConfigSource, load_config_tree};
fn load_source(path: &Path) -> io::Result<ConfigSource<String>> {
let content = fs::read_to_string(path)?;
let includes = content
.lines()
.filter_map(|line| line.strip_prefix("include: "))
.map(PathBuf::from)
.collect();
Ok(ConfigSource::new(content, includes))
}
let tree = load_config_tree("config.yaml", load_source)?;
for node in tree.nodes() {
println!("{}", node.path().display());
}
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
}
Traversierungsregeln
Der Tree-Loader:
- normalisiert Quellpfade lexikalisch;
- weist leere Include-Pfade zurueck;
- loest relative Includes von der Datei auf, die sie deklariert hat;
- behaelt absolute Include-Pfade bei;
- erkennt rekursive Include-Zyklen;
- ueberspringt Dateien, die bereits ueber einen anderen Include-Zweig geladen wurden.
ConfigTreeOptions kann die Traversierung von Geschwister-Includes umkehren:
#![allow(unused)]
fn main() {
use rust_config_tree::{ConfigTreeOptions, IncludeOrder};
let options = ConfigTreeOptions::default().include_order(IncludeOrder::Reverse);
let _ = options;
}
Pfadhelfer
Die Pfadhelfer arbeiten rein lexikalisch. Sie loesen keine symbolischen Links auf und verlangen nicht, dass Pfade existieren:
absolutize_lexical(path)normalize_lexical(path)resolve_include_path(parent_path, include_path)
GitHub Pages
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
Dieses Repository veroeffentlicht das Handbuch mit mdBook und GitHub Pages.
Die Handbuecher der einzelnen Sprachen sind eigenstaendige mdBook-Projekte.
Jede Sprache hat ihr eigenes SUMMARY.md, sodass die linke Seitenleiste nur
Seiten der aktuellen Sprache enthaelt:
rust-config-tree/manual/
en/
book.toml
SUMMARY.md
introduction.md
quick-start.md
...
zh/
book.toml
SUMMARY.md
introduction.md
quick-start.md
...
ja/
book.toml
SUMMARY.md
introduction.md
quick-start.md
...
Lokal bauen mit:
scripts/publish-pages.sh
Die erzeugte Site wird hier geschrieben:
target/mdbook
Veroeffentlichungsworkflow
Der Workflow in .github/workflows/pages.yml laeuft bei Pushes nach main und
bei manueller Ausloesung. Er:
- Checkt das Repository aus.
- Installiert mdBook.
- Fuehrt
scripts/publish-pages.shaus. - Laedt
target/mdbookals Pages-Artefakt hoch. - Stellt das Artefakt auf GitHub Pages bereit.
Die veroeffentlichte URL ist:
https://developerworks.github.io/rust-config-tree/
Crate-Release
Fuer den vollstaendigen Ablauf aus Commit, Push, Pages-Deploy und Crate-Veroeffentlichung:
scripts/release.sh --execute --message "Release 0.1.3"
Verwende den Crate-Release-Helfer aus dem Repository-Root:
scripts/publish-crate.sh
Der Standardmodus fuehrt Pruefungen und cargo publish --dry-run aus. Zum
Veroeffentlichen auf crates.io nach erfolgreichen Pruefungen. Wenn die aktuelle
Version bereits auf crates.io existiert, erhoeht das Skript automatisch die
Patch-Version:
scripts/publish-crate.sh --execute
Die Skriptnutzung ist in scripts/README.md zusammengefasst.