cfg-rs is a lightweight, flexible configuration loader for Rust applications. It composes multiple sources (files, env, inline maps, random, etc.), supports live refresh, placeholder expansion, and derive-based typed configs — all without a serde dependency.
See the examples directory for end-to-end demos.
- Single call to load typed config: see Configuration::get
- Derive your config types: see FromConfig
- Default values via field attributes: see field attributes
- Placeholder expansion like
${cfg.key}: see ConfigValue - Random values under the
randfeature (e.g.configuration.get::<u8>("random.u8")) - Refreshable values via RefValue and refreshable Configuration
- Pluggable sources with clear priority: see register_source1
- No serde dependency
Built-in file parsers (enable via Cargo features):
toml: extensions.toml,.tmlyaml: extensions.yaml,.ymljson: extension.jsonini: extension.ini
Other useful features:
rand: random value provider (e.g.random.u8,random.string)log: minimal logging integration for value parsingcoarsetime: coarse time helpers for time-related valuesregex: regex validation support for#[validate(regex = ...)]
Add to your Cargo.toml with the features you need:
[dependencies]
cfg-rs = { version = "^0.6", features = ["toml"] }For a batteries-included setup, use the convenience feature set:
cfg-rs = { version = "^0.6", features = ["full"] }use cfg_rs::*;
let configuration = Configuration::with_predefined().unwrap();
// use configuration.get::<T>("your.key") or derive types (see below)See PredefinedConfigurationBuilder::init for details.
use cfg_rs::*;
init_cargo_env!();
let configuration = Configuration::with_predefined_builder()
.set_cargo_env(init_cargo_env())
.init()
.unwrap();use cfg_rs::*;
init_cargo_env!();
let mut configuration = Configuration::new()
// Layer 0: Cargo env source.
.register_source(init_cargo_env()).unwrap()
// Layer 1: Inline key-values.
.register_kv("inline")
.set("hello", "world")
.finish()
.unwrap();
// Layer 2: Random values (feature = "rand").
#[cfg(feature = "rand")]
{
configuration = configuration.register_random().unwrap();
}
// Layer 3: All environment variables with prefix `CFG_`.
configuration = configuration.register_prefix_env("CFG").unwrap();
// Layer 4: File(s) — extension inferred by feature (e.g. yaml).
configuration = configuration.register_file("/conf/app.yaml", true).unwrap();
// Optional: register an inline file content (e.g. TOML) and merge.
#[cfg(feature = "toml")]
{
let toml = inline_source!("app.toml").unwrap();
configuration = configuration.register_source(toml).unwrap();
}
// Finally use it.
// let port: u16 = configuration.get("server.port").unwrap();See register_kv, register_file, register_random, and register_prefix_env.
- From inline map (macro):
#[derive(Debug, cfg_rs::FromConfig)]
struct AppCfg { port: u16, host: String }
let cfg: AppCfg = cfg_rs::from_static_map!(AppCfg, {
"port" => "8080",
"host" => "localhost",
});- From environment variables:
#[derive(Debug, cfg_rs::FromConfig)]
struct AppCfg { port: u16, host: String }
unsafe {
std::env::set_var("CFG_APP_PORT", "8080");
std::env::set_var("CFG_APP_HOST", "localhost");
}
let cfg: AppCfg = cfg_rs::from_env("CFG_APP").unwrap();Implement strong-typed configs via derive:
#[derive(Debug, cfg_rs::FromConfig)]
#[config(prefix = "cfg.app")] // optional, implements FromConfigWithPrefix
struct AppCfg {
port: u16, // required
#[config(default = true)]
enabled: bool, // has default value
#[config(name = "ip")] // remap field name
host: String,
}Attributes summary:
#[config(prefix = "cfg.app")]on struct: implementFromConfigWithPrefix#[config(name = "...")]on field: rename field key#[config(default = <expr>)]on field: default value when missing
See the full reference in derive.FromConfig.
The derive macro supports field-level validation via #[validate(...)].
The rules are implemented in src/validate.rs and are
invoked after parsing field values.
Available validators:
range(min = <expr>, max = <expr>)for comparable valueslength(min = <usize>, max = <usize>)for string/collection/path lengthnot_emptyfor any type implementingValidateLengthregex = "..."(feature =regex) for regex matching on strings (pattern can be a string literal orconst &str)custom = "path::to::fn"for user-defined validation
Note: cfg(feature = "...") checks features of your crate, not dependency features. If you want to gate regex/email fields, define a feature in your crate that enables cfg-rs/regex (e.g. regex = ["cfg-rs/regex"]) and use that feature in #[cfg(...)].
Example:
#[cfg(feature = "regex")]
const USER_RE: &str = "^u[a-z]+$";
#[derive(Debug, cfg_rs::FromConfig)]
#[config(prefix = "app")]
struct AppCfg {
#[validate(range(min = 1, max = 65535))]
port: u16,
#[validate(length(min = 1, max = 32))]
name: String,
#[validate(custom = check_threads)]
threads: usize,
#[cfg(feature = "regex")]
#[validate(regex = USER_RE)]
user: String,
}
fn check_threads(v: &usize) -> Result<(), String> {
if *v == 0 {
return Err("threads must be > 0".to_string());
}
Ok(())
}- Placeholder expansion: use
${some.key}inside string values; see ConfigValue - Random values: under
rand, keys likerandom.u8,random.stringprovide per-read randoms - Refreshing:
Configuration::refresh()re-reads sources that allow refresh;RefValue<T>updates on refresh
Browse runnable examples covering common patterns:
simple: minimal setup (full feature set)profile: working with profiles (requirestoml)watch: basic file watching and refresh (requiresyaml)refresh: manual refresh andRefValuelogger: logging integration (requiresfull)thread_pool,salak,test_suit: larger samples and integrations
https://github.com/leptonyu/cfg-rs/tree/main/examples
MIT © contributors. See LICENSE.
This crate supports Rust 1.85 and newer. Older Rust versions are not guaranteed to compile or be tested.
- Source priority is deterministic: earlier registrations override later ones1
- This crate intentionally does not depend on serde
- Docs.rs builds enable all features for a comprehensive reference