rust-config-tree
English | 中文 | 日本語 | 한국어 | Français | Deutsch | Español | Português | Svenska | Suomi | Nederlands
rust-config-tree provides configuration-tree loading and CLI helpers for Rust
applications that use layered config files.
Project manual: https://developerworks.github.io/rust-config-tree/. Language-specific manuals are published as independent mdBook sites with language switch links.
It handles:
- loading a
confiqueschema into a directly usable config object through Figment runtime providers config-template,completions, andinstall-completionscommand handlers- Draft 7 root and section JSON Schema generation for editor completion and validation
- config template generation for YAML, TOML, JSON, and JSON5
- schema directives for TOML and YAML templates without adding runtime fields
- recursive include traversal
.envloading before environment values are merged- source tracking through Figment metadata
- TRACE-level source tracking logs through
tracing - relative include paths resolved from the file declaring them
- lexical path normalization
- include cycle detection
- deterministic traversal order
- mirrored template target collection
- opt-in YAML template splitting for nested schema sections
Applications provide their schema by deriving confique::Config and
implementing ConfigSchema to expose the schema's include field.
Install
[]
= "0.1"
= { = "0.4", = ["yaml", "toml", "json5"] }
= { = "0.10", = ["yaml", "env"] }
= { = "1", = ["derive"] }
= { = "1", = ["derive"] }
= { = "4", = ["derive"] }
Configuration Schema
Your application schema owns the include field. rust-config-tree only needs a
small adapter that extracts includes from the intermediate confique layer.
use PathBuf;
use Config;
use JsonSchema;
use ConfigSchema;
Relative include paths are resolved from the file that declares them:
# config.yaml
include:
- config/server.yaml
mode: shadow
# config/server.yaml
server:
port: 7777
Load the final schema with load_config:
use load_config;
load_config loads the first .env file found by walking upward from the root
config file's directory before asking Figment to read schema-declared
environment variables. Values already present in the process environment are
preserved and take precedence over .env values.
Runtime config loading is performed through Figment. confique remains
responsible for schema metadata, defaults, validation, and template generation.
Environment variable names are read from #[config(env = "...")]; the loader
does not use Env::split("_") or Env::split("__"), so a variable such as
APP_DATABASE_POOL_SIZE can map to a field named database.pool_size.
load_config does not read command-line arguments because CLI flags are
application-specific. Add CLI overrides by merging a provider after
build_config_figment, then validate with load_config_from_figment:
CLI flag names are not derived from config paths. Use normal application flags
such as --server-port or --database-url; do not rely on --server.port or
a.b.c unless the application deliberately implements that parser. The nested
serialized override shape decides which config key is overridden.
Only values represented in the application's CliOverrides provider can
override configuration. This is intended for frequently adjusted runtime
parameters, where changing a flag for one run is better than editing a config
file. Keep stable configuration in files and expose only deliberate temporary
overrides as CLI flags.
use Serialized;
use Serialize;
use ;
With CLI overrides merged this way, runtime precedence is:
command-line overrides
> environment variables
> config files
> confique code defaults
Use load_config_with_figment when the caller needs source metadata:
use load_config_with_figment;
The loader also emits config source tracking with tracing::trace!. Those
events are produced only when TRACE is enabled by the application's tracing
subscriber. If tracing is initialized after config loading, call
trace_config_sources::<AppConfig>(&figment) after installing the subscriber.
Template Generation
Templates are rendered with the same schema and include traversal rules. The output format is inferred from the output path:
.yamland.ymlgenerate YAML.tomlgenerates TOML.jsonand.json5generate JSON5-compatible templates- unknown or missing extensions generate YAML
Use write_config_schemas to create Draft 7 JSON Schemas for the root config
and split nested sections. Mark a nested field with
#[schemars(extend("x-tree-split" = true))] when it should be generated as its
own config/*.yaml and schemas/*.schema.json pair. Unmarked nested fields
stay in the parent template and parent schema. The generated schemas omit
required constraints so IDEs can offer completion for partial config files
without reporting missing fields:
use write_config_schemas;
For a schema with server and log sections marked with x-tree-split, this writes
schemas/myapp.schema.json, schemas/server.schema.json, and
schemas/log.schema.json. The root schema contains only fields that belong in
the root config file, such as include and root scalar fields. It intentionally
omits split nested section properties, so server and log are completed only
when editing their own section YAML files. Nested sections without
x-tree-split remain in the root schema because they do not have independent
template or schema files.
Use write_config_templates to create a root template and every template file
reachable from its include tree:
use write_config_templates;
Use write_config_templates_with_schema when generated TOML and YAML templates
should bind those schemas for IDE completion and validation:
use write_config_templates_with_schema;
Root TOML/YAML targets bind the root schema and do not complete split child
section fields. Split section YAML targets bind their matching section schema, for
example config/log.yaml receives
# yaml-language-server: $schema=../schemas/log.schema.json. JSON and JSON5
targets intentionally do not receive a $schema field; bind them with editor
settings such as VS Code json.schemas.
Template generation chooses its source tree in this order:
- an existing config path
- an existing output template path
- the output path, treated as a new empty template tree
If a source node has no include list, rust-config-tree derives child template
files from nested confique sections marked with x-tree-split. With the
schema above, an empty config.example.yaml source produces:
config.example.yaml
config/server.yaml
The root template receives an include block for config/server.yaml. YAML
targets that map to a nested section, such as config/server.yaml, contain only
that section. Unmarked nested sections stay inline in their parent template.
Further nested sections are split recursively when those fields also carry
x-tree-split.
Override template_path_for_section when a section should be generated at a
different path:
use PathBuf;
use Config;
use JsonSchema;
use ConfigSchema;
The default section path is config/<section>.yaml for top-level nested
sections. Nested children are placed under their parent file stem, for example
config/trading/risk.yaml.
CLI Integration
Flatten ConfigCommand into your existing clap command enum to add:
config-templateconfig-schemaconfig-validatecompletionsinstall-completions
The consuming application keeps its own Parser type and its own command enum.
rust-config-tree only contributes reusable subcommands:
- Add
#[command(subcommand)] command: Commandto the application's parser. - Add
#[command(flatten)] Config(ConfigCommand)to the application'sSubcommandenum. - Clap expands the flattened variants into the same subcommand level as the application's own commands.
- Match that variant and call
handle_config_command::<Cli, AppConfig>.
Application-specific config override flags stay on the application's own parser.
For example, --server-port can map to server.port by building a nested
CliOverrides { server: Some(CliServerOverrides { port }) } value and merging
it with Serialized::defaults.
use PathBuf;
use ;
use Config;
use JsonSchema;
use ;
config-template --output <path> writes templates to the selected path. If no
output path is provided, it writes config.example.yaml in the current
directory. Add --schema <path> to bind TOML and YAML templates to a generated
JSON Schema set without adding a runtime $schema field. This also writes the
root schema and section schemas to the selected schema path.
config-schema --output <path> writes the root Draft 7 JSON Schema and
section schemas. If no output path is provided, the root schema is written to
schemas/config.schema.json.
config-validate loads the full runtime config tree and runs confique
defaults and validation. Use editor schemas for non-noisy completion while
editing split files; use this command for required fields and final config
validation. It prints Configuration is ok when validation succeeds.
completions <shell> prints completions to stdout.
install-completions <shell> writes completions under the user's home
directory and updates the shell startup file when the shell requires it. Bash,
Elvish, Fish, PowerShell, and Zsh are supported.
Lower-Level Tree API
Use load_config_tree when you do not use confique or when you need direct
access to traversal results:
use ;
use ;
The tree API normalizes paths lexically, rejects empty include paths, detects recursive include cycles, and skips files that were already loaded through another include branch.
License
Licensed under either of:
- Apache License, Version 2.0
- MIT license
at your option.