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_configoderconfig-validate. - 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.1"
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. Fuehre CLI-Ueberschreibungen nach build_config_figment
zusammen, wenn die Anwendung Flags fuer Konfigurationsueberschreibungen hat.
CLI-Flag-Namen werden von der Anwendung gewaehlt. Sie sind nicht automatisch
a.b.c-Konfigurationspfade. Bevorzuge normale clap-Flags wie --server-port
und bilde sie dann auf eine verschachtelte Ueberschreibungsstruktur ab. Die
verschachtelte serialisierte Form steuert den ueberschriebenen
Konfigurationsschluessel.
Nur Werte, die im CliOverrides-Provider der Anwendung dargestellt sind,
ueberschreiben Konfiguration. Das ist nuetzlich fuer Parameter, die haeufig fuer
einen einzelnen Lauf geaendert werden, ohne die Konfigurationsdatei zu
bearbeiten. Stabile Werte sollten in Konfigurationsdateien bleiben.
#![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>>(())
}
Mit so zusammengefuehrten CLI-Ueberschreibungen ist die vollstaendige Prioritaet:
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 config-validate 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 config-validate 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 config-validate 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:
config-templateconfig-schemaconfig-validatecompletionsinstall-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 config-template
Der Befehl schreibt Vorlagen unter config/<root_config_name>/. Wenn --output
einen Pfad erhaelt, wird nur der Dateiname verwendet. Wenn kein
Ausgabe-Dateiname 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 config-template --output app_config.example.toml --schema schemas/myapp.schema.json
Root- und Abschnitts-JSON-Schemas erzeugen:
demo config-schema
Ohne --output schreibt config-schema das Root-Schema nach
config/<root_config_name>/<root_config_name>.schema.json.
Den vollstaendigen Laufzeit-Konfigurationsbaum validieren:
demo config-validate
Erzeugte Editor-Schemas vermeiden bewusst Pflichtfeld-Diagnosen fuer
aufgeteilte Dateien. config-validate 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 -- config-template
cargo run --example config_commands -- config-schema
cargo run --example config_commands -- config-validate
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:
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.