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

Manuel rust-config-tree

English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands

Ceci est le manuel francais de rust-config-tree.

Commencez par Introduction, Demarrage rapide ou les Exemples executables.

Introduction

English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands

rust-config-tree fournit un chargement reutilisable d’arbres de configuration et des assistants CLI pour les applications Rust qui utilisent des fichiers de configuration en couches.

La crate est concue autour d’une petite separation des responsabilites :

  • confique possede les definitions de schema, les valeurs par defaut du code, la validation et la generation de modeles de configuration.
  • figment possede le chargement d’execution et les metadonnees de source d’execution.
  • rust-config-tree possede la traversee recursive des inclusions, la resolution des chemins d’inclusion, le chargement de .env, la decouverte des cibles de modeles et les commandes clap reutilisables.

La crate est utile lorsqu’une application veut une disposition naturelle des fichiers de configuration comme celle-ci :

include:
  - config/server.yaml
  - config/database.yaml

log:
  level: info

Chaque fichier inclus peut utiliser la meme forme de schema, et les chemins d’inclusion relatifs sont resolus depuis le fichier qui les a declares. La configuration finale reste une valeur de schema confique normale.

Fonctionnalites principales

  • Traversee recursive des inclusions avec detection des cycles.
  • Chemins d’inclusion relatifs resolus depuis le fichier declarant.
  • Chargement de .env avant l’evaluation des fournisseurs d’environnement.
  • Variables d’environnement declarees par le schema sans separation par delimiteur.
  • Metadonnees Figment pour le suivi des sources d’execution.
  • Evenements de suivi des sources au niveau TRACE via tracing.
  • Generation de schemas JSON Draft 7 pour la completion et les controles de schema de base dans l’editeur.
  • Validation des valeurs de champ dans le code applicatif avec #[config(validate = Self::validate)], executee par load_config ou config-validate.
  • Generation de modeles YAML, TOML, JSON et JSON5.
  • Directives de schema TOML #:schema, modelines YAML Language Server et champs JSON/JSON5 $schema pour les modeles generes.
  • Decoupage opt-in des modeles YAML pour les sections marquees x-tree-split.
  • Sous-commandes clap integrees pour les modeles de configuration, les schemas JSON et les completions shell.
  • API d’arbre de plus bas niveau pour les appelants qui n’utilisent pas confique.

Points d’entree publics

Utilisez ces API pour la plupart des applications :

  • load_config::<S>(path) charge le schema final.
  • load_config_with_figment::<S>(path) charge le schema et renvoie le graphe Figment utilise pour le suivi des sources.
  • write_config_templates::<S>(config_path, output_path) ecrit le modele racine et les modeles enfants decouverts recursivement.
  • write_config_schemas::<S>(output_path) ecrit les schemas JSON Draft 7 racine et de section.
  • handle_config_command::<Cli, S>(command, config_path) gere les commandes de configuration clap integrees.

Utilisez load_config_tree lorsque vous avez besoin de la primitive de traversee sans confique.

Demarrage rapide

English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands

Ajoutez la crate et les bibliotheques de schema/execution utilisees par votre application :

[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"] }

Definissez un schema confique et implementez ConfigSchema pour le type racine :

#![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()
    }
}
}

Chargez la configuration :

#![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>>(())
}

Utilisez un fichier racine avec des inclusions recursives :

# config.yaml
include:
  - config/server.yaml
# config/server.yaml
server:
  bind: 0.0.0.0
  port: 3000

La priorite par defaut de load_config est :

environment variables
  > config files, with later merged files overriding earlier files
    > confique code defaults

Lorsque les inclusions sont chargees par l’API de haut niveau, le fichier racine a la priorite de fichier la plus elevee. Les fichiers inclus fournissent des valeurs de priorite plus faible et peuvent servir de valeurs par defaut ou de fichiers propres a une section.

Les arguments de ligne de commande sont propres a chaque application, donc load_config ne les lit pas automatiquement. Fusionnez les remplacements CLI apres build_config_figment lorsque l’application possede des drapeaux de remplacement de configuration :

Les noms de drapeaux CLI sont choisis par l’application. Ils ne sont pas automatiquement des chemins de configuration a.b.c. Preferez des drapeaux clap normaux comme --server-port, puis mappez-les dans une structure de remplacement imbriquee. La forme serialisee imbriquee controle la cle de configuration remplacee.

Seules les valeurs representees dans le fournisseur CliOverrides de l’application remplacent la configuration. C’est utile pour les parametres modifies frequemment pour une seule execution sans modifier le fichier de configuration. Les valeurs stables doivent rester dans les fichiers de configuration.

#![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>>(())
}

Avec des remplacements CLI fusionnes ainsi, la priorite complete est :

command-line overrides
  > environment variables
    > config files
      > confique code defaults

Schema de configuration

English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands

Les schemas d’application sont des types de configuration confique normaux. Le schema racine doit implementer ConfigSchema afin que rust-config-tree puisse decouvrir les inclusions recursives depuis la couche intermediaire confique.

#![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()
    }
}
}

Champ d’inclusion

Le champ d’inclusion peut avoir n’importe quel nom. rust-config-tree ne le connait qu’au travers de ConfigSchema::include_paths.

Le champ doit normalement avoir une valeur par defaut vide :

#![allow(unused)]
fn main() {
#[config(default = [])]
include: Vec<PathBuf>,
}

Le chargeur recoit une couche partiellement chargee pour chaque fichier. Cela lui permet de decouvrir les fichiers de configuration enfants avant que le schema final soit fusionne et valide.

Sections imbriquees

Utilisez #[config(nested)] pour les sections structurees. Les sections imbriquees sont toujours utilisees pour le chargement d’execution. Ajoutez #[schemars(extend("x-tree-split" = true))] lorsqu’un champ imbrique doit aussi etre genere comme son propre modele *.yaml et schema <section>.schema.json :

#![allow(unused)]
fn main() {
#[derive(Debug, Config, JsonSchema)]
struct AppConfig {
    #[config(nested)]
    #[schemars(extend("x-tree-split" = true))]
    server: ServerConfig,
}
}

La forme YAML naturelle est :

server:
  bind: 127.0.0.1
  port: 8080

Champs reserves aux variables d environnement

Marquez un champ feuille avec #[schemars(extend("x-env-only" = true))] lorsque sa valeur doit venir uniquement d une variable d environnement et ne doit pas apparaitre dans les fichiers de configuration generes. Les modeles YAML et schemas JSON generes omettent les champs env-only, et les objets parents devenus vides sont supprimes.

#![allow(unused)]
fn main() {
#[config(env = "APP_SECRET")]
#[schemars(extend("x-env-only" = true))]
secret: String,
}

Validation des valeurs de champ

Les fichiers *.schema.json generes servent uniquement a la completion IDE et aux controles d’editeur de base. Ils ne decident pas si une valeur de champ concrete est valide pour l’application.

La validation des valeurs doit etre implementee dans le code avec #[config(validate = Self::validate)]. Ce validateur s’execute quand la configuration finale est chargee par load_config ou verifiee par config-validate.

Remplacements de chemin de section pour les modeles

Lorsqu’une source de modele n’a pas d’inclusions, la crate peut deriver les fichiers modeles enfants depuis les sections de schema imbriquees marquees x-tree-split. Le chemin de premier niveau par defaut est <section>.yaml.

Remplacez ce chemin avec 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,
        }
    }
}
}

Chargement d’execution

English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands

Le chargement d’execution est volontairement separe entre Figment et confique :

figment:
  runtime file loading
  runtime environment loading
  runtime source metadata

confique:
  schema metadata
  defaults
  validation
  config templates

L’API principale est :

#![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>>(())
}

Utilisez load_config_with_figment lorsque l’application a besoin des metadonnees de source :

#![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>>(())
}

Etapes de chargement

Le chargeur de haut niveau effectue ces etapes :

  1. Resoudre lexicalement le chemin de configuration racine.
  2. Charger le premier fichier .env trouve en remontant depuis le repertoire de configuration racine.
  3. Charger chaque fichier de configuration comme couche partielle pour decouvrir les inclusions.
  4. Construire un graphe Figment depuis les fichiers de configuration decouverts.
  5. Fusionner ConfiqueEnvProvider avec une priorite superieure aux fichiers.
  6. Fusionner eventuellement les remplacements CLI propres a l’application.
  7. Extraire une couche confique depuis Figment.
  8. Appliquer les valeurs par defaut du code confique.
  9. Valider et construire le schema final.

load_config et load_config_with_figment effectuent les etapes 1-5 et 7-9. L’etape 6 est propre a l’application, car cette crate ne peut pas deduire comment un drapeau CLI correspond a un champ de schema.

Formats de fichier

Le fournisseur de fichier d’execution est choisi depuis l’extension du chemin de configuration :

  • .yaml et .yml utilisent YAML.
  • .toml utilise TOML.
  • .json et .json5 utilisent JSON.
  • les extensions inconnues ou absentes utilisent YAML.

La generation de modeles utilise toujours les renderers de modeles confique pour les sorties YAML, TOML et compatibles JSON5.

Priorite des inclusions

Le chargeur de haut niveau fusionne les fournisseurs de fichiers de sorte que les fichiers inclus aient une priorite plus faible que le fichier qui les a inclus. Le fichier de configuration racine a la priorite de fichier la plus elevee.

Les variables d’environnement ont une priorite superieure a tous les fichiers de configuration. Les valeurs par defaut confique ne sont utilisees que pour les valeurs non fournies par les fournisseurs d’execution.

Lorsque des remplacements CLI sont fusionnes apres build_config_figment, la priorite complete est :

command-line overrides
  > environment variables
    > config files
      > confique code defaults

La syntaxe de ligne de commande n’est pas definie par rust-config-tree. Un drapeau comme --server-port peut remplacer server.port si l’application mappe cette valeur analysee dans un fournisseur serialise imbrique. Une syntaxe avec points comme --server.port ou a.b.c n’existe que si l’application l’implemente.

Cela signifie que la priorite CLI ne s’applique qu’aux cles presentes dans le fournisseur de remplacement de l’application. Utilisez-la pour les valeurs operationnelles qui changent souvent pour une seule execution. Laissez la configuration durable dans les fichiers.

#![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>>(())
}

Variables d’environnement

English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands

Les noms de variables d’environnement sont declares dans le schema avec confique :

#![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 lit ces noms depuis confique::Config::META et construit un fournisseur Figment qui associe chaque variable d’environnement a son chemin de champ exact.

N’utilisez pas le mapping d’environnement Figment base sur des delimiteurs avec cette crate :

#![allow(unused)]
fn main() {
// Do not use this pattern for rust-config-tree schemas.
Env::prefixed("APP_").split("_")
Env::prefixed("APP_").split("__")
}

split("_") traite les underscores comme des separateurs de cles imbriquees. Cela transforme APP_DATABASE_POOL_SIZE en un chemin comme database.pool.size, ce qui entre en conflit avec les noms de champs Rust comme pool_size.

Avec ConfiqueEnvProvider, ce mapping est explicite :

APP_DATABASE_POOL_SIZE -> database.pool_size

Les underscores simples restent une partie du nom de variable d’environnement. Figment ne devine pas la regle d’imbrication.

Chargement dotenv

Avant l’evaluation des fournisseurs d’execution, le chargeur cherche un fichier .env en remontant depuis le repertoire du fichier de configuration racine.

Les variables d’environnement deja presentes dans le processus sont conservees. Les valeurs de .env ne remplissent que les variables d’environnement manquantes.

Exemple :

APP_SERVER_PORT=9000
APP_DATABASE_POOL_SIZE=64

Ces variables remplacent les valeurs des fichiers de configuration lorsque le schema declare des attributs #[config(env = "...")] correspondants.

Analyse des valeurs

Le fournisseur passerelle laisse Figment analyser les valeurs d’environnement. Il n’appelle pas les hooks parse_env de confique. Gardez les valeurs complexes dans les fichiers de configuration sauf si la syntaxe de valeur d’environnement Figment convient au type.

Suivi des sources

English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands

Utilisez load_config_with_figment pour conserver le graphe Figment utilise par le chargement d’execution :

#![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>>(())
}

La valeur Figment renvoyee peut repondre aux questions de source pour les valeurs d’execution :

#![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}");
}
}

Pour les valeurs fournies par ConfiqueEnvProvider, l’interpolation renvoie le nom natif de la variable d’environnement declaree dans le schema :

database.pool_size came from APP_DATABASE_POOL_SIZE

Evenements TRACE

Le chargeur emet des evenements de suivi des sources avec tracing::trace!. Il ne le fait que lorsque TRACE est active :

#![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>>(())
}

Chaque evenement utilise la cible rust_config_tree::config et inclut :

  • config_key : la cle de configuration avec points.
  • source : les metadonnees de source rendues.

Les valeurs qui viennent uniquement des valeurs par defaut confique n’ont pas de metadonnees Figment d’execution. Elles sont signalees comme confique default or unset optional field.

Generation de modeles

English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands

Les modeles sont generes depuis le meme schema confique que celui utilise a l’execution. confique rend le contenu reel du modele, y compris les commentaires de documentation, les valeurs par defaut, les champs obligatoires et les noms de variables d’environnement declares.

Utilisez 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>>(())
}

Generez des schemas JSON Draft 7 pour la configuration racine et les sections imbriquees :

#![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>>(())
}

Marquez un champ imbrique avec #[schemars(extend("x-tree-split" = true))] lorsqu’il doit etre genere comme son propre modele *.yaml et son propre schema <section>.schema.json. Les champs imbriques non marques restent dans le modele parent et le schema parent.

Marquez un champ feuille avec #[schemars(extend("x-env-only" = true))] lorsque la valeur doit venir uniquement de variables d environnement. Les modeles generes et les schemas JSON omettent les champs env-only, et les objets parents devenus vides sont supprimes.

Les schemas generes omettent les contraintes required. Les IDE peuvent toujours proposer la completion, mais les fichiers partiels comme log.yaml ne signalent pas de champs racine manquants. Le schema racine ne complete que les champs qui appartiennent au fichier racine ; les champs de sections imbriquees y sont omis et sont completes par leurs propres schemas de section. Les champs presents peuvent encore recevoir des controles d’editeur de base, comme les types, les enums et les proprietes inconnues pris en charge par le schema genere. Les *.schema.json generes ne decident pas si une valeur de champ concrete est valide pour l’application. La validation de valeur doit etre implementee dans le code avec #[config(validate = Self::validate)] ; load_config et config-validate executent cette validation d’execution.

Liez ces schemas depuis les modeles TOML, YAML, JSON et JSON5 generes :

#![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>>(())
}

Les modeles racine TOML et YAML lient le schema racine et ne completent pas les champs des sections enfants. Les modeles YAML de section separee lient leur schema de section. Les modeles JSON et JSON5 recoivent un champ racine $schema que VS Code peut reconnaitre. VS Code json.schemas reste une autre facon de lier le schema.

Le format de sortie est deduit du chemin de sortie :

  • .yaml et .yml generent du YAML.
  • .toml genere du TOML.
  • .json et .json5 generent des modeles compatibles JSON5.
  • les extensions inconnues ou absentes generent du YAML.

Liaisons de schema

Avec un chemin de schema schemas/myapp.schema.json, les modeles racine generes utilisent :

#:schema ./schemas/myapp.schema.json
# yaml-language-server: $schema=./schemas/myapp.schema.json

Les modeles de section generes lient les schemas de section :

# log.yaml
# yaml-language-server: $schema=./schemas/log.schema.json

Les modeles JSON et JSON5 generes ecrivent un champ racine $schema reconnu par VS Code. Les parametres d’editeur restent optionnels :

{
  "json.schemas": [
    {
      "fileMatch": [
        "/config.json",
        "/config.*.json"
      ],
      "url": "./schemas/myapp.schema.json"
    }
  ]
}

Selection de la source des modeles

La generation de modeles choisit son arbre source dans cet ordre :

  1. Chemin de configuration existant.
  2. Chemin de modele de sortie existant.
  3. Chemin de sortie traite comme nouvel arbre de modeles vide.

Cela permet a un projet de mettre a jour les modeles depuis les fichiers de configuration actuels, de mettre a jour un ensemble de modeles existant ou de creer un nouvel ensemble de modeles uniquement depuis le schema.

Arbres d’inclusion miroirs

Si le fichier source declare des inclusions, les modeles generes reproduisent ces chemins d’inclusion sous le repertoire de sortie.

# config.yaml
include:
  - server.yaml

Generer config.example.yaml ecrit :

config.example.yaml
server.yaml

Les cibles d’inclusion relatives sont reproduites sous le repertoire parent du fichier de sortie. Les cibles d’inclusion absolues restent absolues.

Decoupage opt-in des sections

Lorsqu’un fichier source n’a pas d’inclusions, la crate peut deriver les cibles d’inclusion depuis les sections de schema imbriquees marquees x-tree-split. Pour un schema avec une section server marquee, une source de modele racine vide peut produire :

config.example.yaml
server.yaml

Le modele racine recoit un bloc d’inclusion, et server.yaml ne contient que la section server. Les sections imbriquees ne sont decoupees recursivement que lorsque ces champs portent aussi x-tree-split.

Completions IDE

English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands

Les schemas JSON generes peuvent etre utilises par les fichiers de configuration TOML, YAML, JSON et JSON5. Ils sont generes depuis le meme type Rust que celui utilise par confique :

#![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,
}
}

Generez-les avec :

#![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>>(())
}

Cela ecrit le schema racine et les schemas de section comme schemas/server.schema.json. Les schemas generes omettent les contraintes required afin que la completion fonctionne pour les fichiers de configuration partiels sans diagnostics de champs manquants. Le schema racine omet les proprietes de sections imbriquees, donc la completion des sections enfants n’est disponible que dans les fichiers qui lient le schema de section correspondant.

Les champs marques x-env-only sont omis des schemas generes, donc les IDE ne suggerent pas les secrets ou autres valeurs qui doivent venir uniquement de variables d environnement.

Les schemas IDE servent a la completion et aux controles d’editeur de base, comme les types, les enums et les controles de proprietes inconnues pris en charge par le schema genere. Ils ne decident pas si une valeur de champ concrete est valide pour l’application. La validation de valeur doit etre implementee dans le code avec #[config(validate = Self::validate)], puis executee par load_config ou config-validate. Les champs obligatoires et la validation finale de la configuration fusionnee utilisent aussi ces chemins d’execution.

TOML

Les fichiers TOML doivent lier le schema avec une directive #:schema en haut du fichier :

#:schema ./schemas/myapp.schema.json

[server]
bind = "0.0.0.0"
port = 3000

N’utilisez pas de champ racine $schema = "..." dans TOML. Il devient une donnee de configuration reelle et peut affecter la deserialisation d’execution. write_config_templates_with_schema ajoute automatiquement la directive #:schema pour les modeles TOML.

YAML

Les fichiers YAML doivent utiliser la modeline YAML Language Server :

# yaml-language-server: $schema=./schemas/myapp.schema.json

server:
  bind: 0.0.0.0
  port: 3000

write_config_templates_with_schema ajoute automatiquement cette modeline pour les modeles YAML. Les modeles YAML separes lient leur schema de section, par exemple log.yaml lie ./schemas/log.schema.json.

JSON

Les fichiers JSON et JSON5 peuvent lier un schema avec un champ racine $schema. write_config_templates_with_schema l’ajoute automatiquement aux modeles JSON et JSON5 generes :

{
  "$schema": "./schemas/myapp.schema.json"
}

Les parametres de l’editeur restent utiles si un projet ne veut pas de liaison dans le fichier :

{
  "json.schemas": [
    {
      "fileMatch": [
        "/config.json",
        "/config.*.json",
        "/deploy/*.json"
      ],
      "url": "./schemas/myapp.schema.json"
    }
  ]
}

YAML peut aussi etre lie via les parametres VS Code :

{
  "yaml.schemas": {
    "./schemas/myapp.schema.json": [
      "config.yaml",
      "config.*.yaml",
      "deploy/*.yaml"
    ]
  }
}

La disposition finale est :

schemas/myapp.schema.json:
  Champs du fichier racine uniquement

schemas/server.schema.json:
  Schema de la section 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"

References :

Integration CLI

English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands

ConfigCommand fournit des sous-commandes clap reutilisables :

  • config-template
  • config-schema
  • config-validate
  • completions
  • install-completions
  • uninstall-completions

Ces sous-commandes integrees sont separees des drapeaux de remplacement de configuration propres a l’application. Fusionnez les drapeaux de remplacement comme fournisseurs Figment dans le chemin de chargement d’execution.

Les drapeaux de remplacement de configuration restent dans la CLI de l’application consommatrice. Leurs noms n’ont pas besoin de correspondre aux chemins de configuration avec points. Par exemple, l’application peut analyser --server-port et le mapper a la cle de configuration imbriquee server.port. Seuls les drapeaux que l’application mappe dans CliOverrides affectent les valeurs de configuration.

Aplatissez-le dans une enum de commandes d’application :

  1. Gardez le type Parser propre a l’application.
  2. Gardez l’enum Subcommand propre a l’application.
  3. Ajoutez #[command(flatten)] Config(ConfigCommand) a cette enum.
  4. Clap developpe les variantes ConfigCommand aplaties au meme niveau de commande que les variantes propres a l’application.
  5. Faites correspondre la variante Config(command) et passez-la a handle_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(())
}

Modeles de configuration

demo config-template

La commande ecrit les modeles sous config/<root_config_name>/. Si --output recoit un chemin, seul le nom de fichier est utilise. Si aucun nom de fichier de sortie n’est fourni, la commande ecrit config/<root_config_name>/<root_config_name>.example.yaml. Ajoutez --schema schemas/myapp.schema.json pour lier les modeles TOML, YAML, JSON et JSON5 generes aux schemas JSON generes. Les modeles YAML separes lient le schema de section correspondant. Les modeles JSON et JSON5 recoivent un champ $schema reconnu par VS Code. La commande ecrit aussi les schemas racine et de section au chemin de schema choisi.

demo config-template --output app_config.example.toml --schema schemas/myapp.schema.json

Generer les schemas JSON racine et de section :

demo config-schema

Sans --output, config-schema ecrit le schema racine dans config/<root_config_name>/<root_config_name>.schema.json.

Valider l’arbre complet de configuration d’execution :

demo config-validate

Les schemas d’editeur generes evitent intentionnellement les diagnostics de champs obligatoires pour les fichiers separes. config-validate charge les inclusions, applique les valeurs par defaut et lance la validation finale confique, y compris les validateurs declares avec #[config(validate = Self::validate)]. Les *.schema.json generes restent reserves a la completion IDE et aux controles d’editeur de base, pas a la validation de legalite des valeurs. Elle affiche Configuration is ok lorsque la validation reussit.

Completions shell

Imprimer les completions sur stdout :

demo completions zsh

Installer les completions :

demo install-completions zsh

Desinstaller les completions :

demo uninstall-completions zsh

L’installateur prend en charge Bash, Elvish, Fish, PowerShell et Zsh. Il ecrit le fichier de completion sous le repertoire home de l’utilisateur et met a jour le fichier de demarrage du shell pour les shells qui l’exigent.

Avant de modifier un fichier de demarrage shell existant comme ~/.zshrc, ~/.bashrc, un fichier rc Elvish ou un profil PowerShell, la commande ecrit une sauvegarde a cote du fichier original :

<rc-file>.backup.by.<program-name>.<timestamp>

Exemples

English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands

Le depot inclut des exemples executables pour charger des arbres de configuration, appliquer des remplacements CLI, utiliser les commandes de configuration integrees, generer des modeles et utiliser l’API d’arbre de plus bas niveau.

Lisez l’index des exemples du depot :

Executez les exemples depuis la racine du depot :

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

API d’arbre

English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands

Utilisez l’API d’arbre de plus bas niveau lorsque l’application n’utilise pas confique, ou lorsqu’elle a besoin d’un acces direct aux resultats de traversee.

#![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>>(())
}

Regles de traversee

Le chargeur d’arbre :

  • normalise lexicalement les chemins sources ;
  • rejette les chemins d’inclusion vides ;
  • resout les inclusions relatives depuis le fichier qui les a declarees ;
  • preserve les chemins d’inclusion absolus ;
  • detecte les cycles d’inclusion recursifs ;
  • ignore les fichiers deja charges par une autre branche d’inclusion.

ConfigTreeOptions peut inverser la traversee des inclusions soeurs :

#![allow(unused)]
fn main() {
use rust_config_tree::{ConfigTreeOptions, IncludeOrder};

let options = ConfigTreeOptions::default().include_order(IncludeOrder::Reverse);
let _ = options;
}

Assistants de chemin

Les assistants de chemin sont uniquement lexicaux. Ils ne resolvent pas les liens symboliques et n’exigent pas que les chemins existent :

  • 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

Ce depot publie le manuel avec mdBook et GitHub Pages.

Les manuels de chaque langue sont des projets mdBook independants. Chaque langue a son propre SUMMARY.md, donc la barre laterale gauche ne contient que les pages de la langue courante :

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

Construisez localement avec :

scripts/publish-pages.sh

Le site genere est ecrit dans :

target/mdbook

Workflow de publication

Le workflow dans .github/workflows/pages.yml s’execute lors des pushes vers main et en declenchement manuel. Il :

  1. Recupere le depot.
  2. Installe mdBook.
  3. Execute scripts/publish-pages.sh.
  4. Televerse target/mdbook comme artefact Pages.
  5. Deploie l’artefact vers GitHub Pages.

L’URL publiee est :

https://developerworks.github.io/rust-config-tree/

Publication de la crate

Pour le flux complet de commit, push, deploiement Pages et publication de la crate :

scripts/release.sh --execute --message "Release 0.1.3"

Utilisez l’assistant de publication de crate depuis la racine du depot :

scripts/publish-crate.sh

Le mode par defaut lance les controles et cargo publish --dry-run. Pour publier sur crates.io apres la reussite des controles. Si la version courante existe deja sur crates.io, le script incremente automatiquement la version patch :

scripts/publish-crate.sh --execute

L’utilisation des scripts est resumee dans scripts/README.md.