Skip to content

leptonyu/cfg-rs

Repository files navigation

cfg-rs: A Configuration Library for Rust Applications

Crates.io Crates.io Documentation dependency status License Actions Status Minimum supported Rust version

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: https://github.com/leptonyu/cfg-rs/tree/main/examples

Features

Supported formats and feature flags

Built-in file parsers (enable via Cargo features):

  • toml: extensions .toml, .tml
  • yaml: extensions .yaml, .yml
  • json: extension .json
  • ini: extension .ini

Other useful features:

  • rand: random value provider (e.g. random.u8, random.string)
  • log: minimal logging integration for value parsing
  • coarsetime: coarse time helpers for time-related values

Installation

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

Quick start

1) One-liner with predefined sources

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.

2) Customize predefined builder

use cfg_rs::*;
init_cargo_env!();

let configuration = Configuration::with_predefined_builder()
    .set_cargo_env(init_cargo_env())
    .init()
    .unwrap();

3) Compose your own sources (priority = registration order)

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.

4) Handy helpers for tests and small apps

  • 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 }

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();

Derive typed configs

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: implement FromConfigWithPrefix
  • #[config(name = "...")] on field: rename field key
  • #[config(default = <expr>)] on field: default value when missing

See the full reference in derive.FromConfig.

Placeholders, randoms, and refresh

  • Placeholder expansion: use ${some.key} inside string values; see ConfigValue
  • Random values: under rand, keys like random.u8, random.string provide per-read randoms
  • Refreshing: Configuration::refresh() re-reads sources that allow refresh; RefValue<T> updates on refresh

Examples

Browse runnable examples covering common patterns:

  • simple: minimal setup (full feature set)
  • profile: working with profiles (requires toml)
  • watch: basic file watching and refresh (requires yaml)
  • refresh: manual refresh and RefValue
  • logger: logging integration (requires full)
  • thread_pool, salak, test_suit: larger samples and integrations

https://github.com/leptonyu/cfg-rs/tree/main/examples

Minimum supported Rust version

rustc 1.81+

License

MIT © contributors. See LICENSE.

Tips and notes

  • 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

Footnotes

  1. Source precedence follows registration order — earlier registrations have higher priority. 2