| Crates.io | cnfg |
| lib.rs | cnfg |
| version | 0.1.2 |
| created_at | 2025-09-19 15:22:51.768425+00 |
| updated_at | 2025-09-21 11:49:26.606891+00 |
| description | Unified, validated configuration for Rust |
| homepage | https://github.com/tommantonclery/cnfg |
| repository | https://github.com/tommantonclery/cnfg |
| max_upload_size | |
| id | 1846605 |
| size | 48,964 |
cnfg lets you describe your application's configuration once and load it from files, environment variables, and CLI flags with compile-time guarantees. Derive the Cnfg macro on a normal serde struct and the crate handles source precedence, deserialization, validation, and help text for you.
#[derive(Cnfg)]--help output automatically from doc comments and annotationsAdd the library crate to your Cargo.toml (the derive macro is re-exported, no extra dependency required):
[dependencies]
cnfg = { version = "0.1.1", features = ["yaml", "toml"] }
You can also use cargo add:
cargo add cnfg --features yaml,toml
Feature flags are optional:
| Feature | Default? | Purpose |
|---|---|---|
yaml |
✅ | Enable loading config.yaml / config.yml files |
toml |
✅ | Enable loading config.toml files |
json |
✅ | Enable loading config.json files |
Disable features if you want a smaller dependency tree:
cnfg = { version = "0.1.1", default-features = false, features = ["toml"] }
Create a normal serde struct, derive Cnfg, and annotate each field with the sources you care about.
use cnfg::{Cnfg, CnfgError};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Cnfg)]
struct AppConfig {
/// Name used for logging and help output
#[cnfg(default = "demo-app", cli)]
name: String,
/// Toggle verbose logging (`--debug` or `DEBUG=true`)
#[cnfg(default = false, cli, env = "APP_DEBUG")]
debug: bool,
/// Database connection string (required when not using defaults)
#[cnfg(env = "DATABASE_URL", required)]
database_url: String,
}
fn main() -> Result<(), CnfgError> {
let cfg = AppConfig::load()?;
println!("Loaded config: {cfg:#?}");
Ok(())
}
The generated loader combines all sources in the following order (later sources win):
#[cnfg(default = ...)]CONFIG_FILE env override or config.{toml,yaml,json})#[cnfg(env = "NAME")]#[cnfg(cli)]Missing required values surface as CnfgError::Validation with field-qualified error messages.
Doc comments flow into the generated CLI help. Call AppConfig::help() or run your binary with --help to see output like:
Usage:
<binary> [OPTIONS]
Options:
--name <value> Name used for logging and help output [default: demo-app]
--debug Toggle verbose logging (--debug or DEBUG=true)
When users pass --help, cnfg prints the help text and returns CnfgError::HelpPrinted so you can exit gracefully.
Break large configs into focused structs. Mark nested fields with #[cnfg(nested)] and use #[serde(default)] when the child implements Default.
#[derive(Debug, Serialize, Deserialize, Cnfg)]
struct Database {
#[cnfg(default = "localhost", env = "DB_HOST")]
host: String,
#[cnfg(default = 5432, cli, validate(range(min = "1024", max = "65535")))]
port: u16,
}
#[derive(Debug, Serialize, Deserialize, Cnfg)]
struct AppConfig {
#[serde(default)]
#[cnfg(nested)]
database: Database,
}
Nested required fields are tracked automatically (e.g. database.host) and validation errors are surfaced with fully-qualified paths.
The derive macro ships with common validators:
#[derive(Debug, Serialize, Deserialize, Cnfg)]
struct Limits {
#[cnfg(required, validate(range(min = "1", max = "99")))]
workers: usize,
#[cnfg(validate(regex = "^[a-z0-9_-]+$"))]
cluster: String,
#[cnfg(validate(url))]
callback: String,
}
For custom checks, implement Validate manually or extend with #[cnfg(validate(custom_fn = "path::to::fn"))] (coming soon).
dotenvy support by dropping a .env file next to your binary—cnfg will load it automatically.crates/cnfg/tests/nested.rs).AppConfig::defaults_json() in unit tests to assert default shapes without touching real files.A runnable example lives in examples/tester. It showcases nested configs, CLI help, and validation.
The crate targets Rust 1.70.0 or newer (for OnceLock). CI and docs assume the 2021 edition.
The library is production-ready for building internal tools and services. Feedback is welcome—open an issue or discussion on GitHub.
Licensed under the Apache License, Version 2.0.