| name | clapfig-quick-howto |
| description | Quick, copyable path for adopting Clapfig 0.23 in a Rust CLI project. Use when:
(1) Adding layered configuration (defaults, config file, env vars, CLI flags)
to a clap-based Rust app
(2) Wiring the `config gen|list|get|set|unset|schema` command family
(3) Giving users a persistent settings file (`myapp config set server.port 9090`)
plus one-run CLI overrides (`myapp --port 9090 run`)
|
Clapfig Quick How-To
Clapfig turns plain Rust structs into layered configuration: compiled
defaults, then config files, then env vars, then CLI flags — later layers
win. This is the minimal adoption path for Clapfig 0.23.
1. Install
[dependencies]
clapfig = "0.23"
clap = { version = "4", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
The default features include derive (#[derive(clapfig::Schema)]) and
clap (the config subcommand integration). clap and serde are direct
dependencies of your crate too: the example below derives Parser and
Serialize/Deserialize on your own types, and Cargo does not expose
clapfig's transitive dependencies to your code.
2. Define the config structs — nested, in their owning modules
Each subsystem declares its own config struct in the module that owns
it; the application config composes them as fields. Do not grow one root
struct that spells out every subsystem's details.
use clapfig::Schema;
use serde::{Deserialize, Serialize};
#[derive(Schema, Serialize, Deserialize, Debug)]
pub struct ServerConfig {
#[clapfig(default = "127.0.0.1")]
pub host: String,
#[clapfig(default = 8080)]
pub port: u16,
}
use clapfig::Schema;
use serde::{Deserialize, Serialize};
use crate::server::ServerConfig;
#[derive(Schema, Serialize, Deserialize, Debug)]
pub struct AppConfig {
#[clapfig(default = false)]
pub verbose: bool,
pub server: ServerConfig,
}
What the derive gives you:
#[clapfig(default = ...)] — the compiled default, the lowest layer.
- A nested
Schema field becomes a config-file section ([server] in
TOML), addressable as server.port dotted keys and MYAPP__SERVER__PORT
env vars.
/// doc comments become the documentation in generated templates and
config get output.
- Strict mode is on by default: an unknown key in a config file fails the
load with the file, key, and line.
3. Wire clap: the config command family plus override flags
Embed ConfigArgs as one subcommand and add clap flags for the keys users
override per run:
use clap::{Parser, Subcommand};
use clapfig::{Clapfig, ClapfigError, ConfigArgs, SearchPath, TypedBuilder};
use crate::config::AppConfig;
mod config;
mod server;
#[derive(Parser)]
#[command(name = "myapp")]
struct Cli {
#[arg(long, global = true)]
port: Option<u16>,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Run,
Config(ConfigArgs),
}
fn make_builder(cli: &Cli) -> TypedBuilder<AppConfig> {
Clapfig::typed::<AppConfig>()
.app_name("myapp")
.persist_scope("user", SearchPath::Platform)
.cli_override("server.port", cli.port.map(i64::from))
}
fn main() -> Result<(), ClapfigError> {
let = Cli::();
= (&cli);
cli.command {
Commands::Run => {
= builder.()?;
(, config.server.host, config.server.port);
}
Commands::(args) => {
builder.(&args.())?;
}
}
(())
}
For several top-level overrides, cli_overrides_from(&overrides) serializes
a struct and auto-matches fields to top-level config keys by name; nested
keys still need explicit cli_override("section.key", ...) calls.
4. The two override behaviors users get
Persistent — config set writes the value into the user settings file,
so every future load reads it — though env vars and CLI flags are higher
layers and still win for that key when set:
myapp config set server.port 9090
myapp run
myapp config unset server.port
One-run — the clap flag overrides the key for this process only, on top
of every lower layer; nothing is written:
myapp --port 9090 run
5. The user settings file
persist_scope("user", SearchPath::Platform) names a persistence scope:
- Where —
SearchPath::Platform is the OS config directory (XDG config
home on Linux, ~/Library/... on macOS, AppData on Windows), so the file
lands where users expect app settings.
- Writes —
config set / config unset edit the scope's file. If it
does not exist yet, set creates myapp.toml there, seeded from the
generated template so every field arrives with its doc comment.
- Reads — scope paths are automatically added to the search paths, so
persisted values are always picked up by
load().
- The first scope added is the default; add more (e.g.
.persist_scope("local", SearchPath::Cwd)) and users select one with
--scope.
6. What the command family gives users
myapp config gen
myapp config list
myapp config get server.port
myapp config set server.port 9090
myapp config unset server.port
myapp config schema
For the full details, see the Clapfig repository docs:
docs/getting-started.md, docs/config-command.md,
docs/derive-reference.md, and docs/layered-config.md, plus the runnable
crates/clapfig/examples/clapfig_demo example.