| Crates.io | cfgloader_rs |
| lib.rs | cfgloader_rs |
| version | 1.0.1 |
| created_at | 2025-09-03 10:06:49.245583+00 |
| updated_at | 2025-09-22 03:07:45.222681+00 |
| description | A simple, powerful, and ergonomic configuration loading library for Rust applications |
| homepage | |
| repository | https://github.com/j50301m/cfgloader_rs |
| max_upload_size | |
| id | 1822286 |
| size | 14,131 |
A simple, powerful, and ergonomic configuration loading library for Rust applications. CFGLoader automatically loads configuration from environment variables and .env files with compile-time validation and type safety.
A wrapper around dotenvy that provides the FromEnv derive macro and utilities to simplify the complexity of reading environment variables.
Add cfgloader_rs to your Cargo.toml:
[dependencies]
cfgloader_rs = "1.0"
use cfgloader_rs::*;
#[derive(FromEnv, Debug)]
struct Config {
#[env("DATABASE_URL", default = "sqlite://app.db")]
database_url: String,
#[env("PORT", default = "8080")]
port: u16,
#[env("API_KEY", required)]
api_key: String,
#[env("FEATURES", default = "auth,logging", split = ",")]
features: Vec<String>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// One path
let config = Config::load(std::path::Path::new(".env"))?;
// Or multiple paths
let config2 = Config::load_iter(vec![std::path::Path::new(".env"),std::path::Path::new(".env.local")])?;
println!("Config: {:#?}", config);
Ok(())
}
You can use load_iter to try multiple .env files in order:
let config = Config::load_iter([
std::path::Path::new(".env.local"),
std::path::Path::new(".env"),
])?;
This will try .env.local first, then .env if the first is not found.
pub trait FromEnv: Sized {
fn load(env_path: &std::path::Path) -> Result<Self, CfgError>;
fn load_iter<I, P>(paths: I) -> Result<Self, CfgError>
where
I: IntoIterator<Item = P>,
P: AsRef<std::path::Path>;
}
load(env_path: &Path): Load config from a single .env fileload_iter<I, P>(paths: I): Try multiple paths, return on first success# .env file or environment variables
DATABASE_URL=postgresql://localhost/myapp
PORT=3000
API_KEY=your-secret-key
FEATURES=auth,logging,metrics,cache
use cfgloader_rs::*;
#[derive(FromEnv, Debug)]
struct AppConfig {
server: ServerConfig,
database: DatabaseConfig,
}
#[derive(FromEnv, Debug)]
struct ServerConfig {
#[env("SERVER_HOST", default = "127.0.0.1")]
host: String,
#[env("SERVER_PORT", default = "8080")]
port: u16,
}
#[derive(FromEnv, Debug)]
struct DatabaseConfig {
#[env("DB_URL", required)]
url: String,
#[env("DB_MAX_CONNECTIONS", default = "10")]
max_connections: u32,
}
use cfgloader_rs::*;
#[derive(FromEnv, Debug)]
struct Config {
// Parse comma-separated values
#[env("ALLOWED_HOSTS", default = "localhost,127.0.0.1", split = ",")]
allowed_hosts: Vec<String>,
// Parse numbers
#[env("WORKER_THREADS", default = "1,2,4,8", split = ",")]
worker_threads: Vec<u32>,
// Custom separator
#[env("TAGS", default = "web|api|service", split = "|")]
tags: Vec<String>,
}
use cfgloader_rs::*;
#[derive(FromEnv, Debug)]
struct Config {
// Required - will fail if not provided
#[env("API_KEY", required)]
api_key: String,
// Optional with default
#[env("DEBUG_MODE", default = "false")]
debug_mode: bool,
// Optional without default (uses type's Default implementation)
#[env("OPTIONAL_SETTING")]
optional_setting: String, // Will be empty string if not set
}
#[env("ENV_VAR_NAME")] - Load value from the specified environment variable#[env("ENV_VAR_NAME", default = "value")] - Provide a default value if the environment variable is not set#[env("ENV_VAR_NAME", required)] - Mark a field as required. The application will fail to start if this environment variable is not provided#[env("ENV_VAR_NAME", split = "separator")] - Parse the environment variable as a delimited string and convert to Vec<T>#[env] attributes are treated as nested configuration structsCFGLoader supports any type that implements FromStr:
String, bool, i32, u32, f64, etc.Vec<T> where T: FromStrFromStruse std::str::FromStr;
#[derive(Debug)]
struct LogLevel(String);
impl FromStr for LogLevel {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(LogLevel(s.to_uppercase()))
}
}
#[derive(FromEnv, Debug)]
struct Config {
#[env("LOG_LEVEL", default = "info")]
log_level: LogLevel,
}
CFGLoader provides descriptive error messages:
use cfgloader_rs::*;
#[derive(FromEnv)]
struct Config {
#[env("PORT", required)]
port: u16,
}
fn main() {
match Config::load(std::path::Path::new(".env")) {
Ok(config) => println!("Config loaded successfully!"),
Err(CfgError::MissingEnv(var)) => {
eprintln!("Missing required environment variable: {}", var);
}
Err(CfgError::ParseError { key, value, ty, source }) => {
eprintln!("Failed to parse {} value '{}' as {}: {}", key, value, ty, source);
}
Err(e) => eprintln!("Configuration error: {}", e),
}
}
CFGLoader consists of three main crates:
cfgloader_rs: Main crate that re-exports everything you needcfgloader-core: Core functionality and error typescfgloader_rs_macros: Procedural macros for FromEnv deriveLicensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
To contribute to this project, initialize the repository for development:
make init
This will:
cargo fmt, cargo clippy, and tests before each pushcargo-audit, cargo-outdated, cargo-expand)You can run all CI checks manually with:
make ci # Run all quality checks (fmt, clippy, check, test, doc)
make help # Show available commands
Check out the example directory for a complete working example, or run:
cd example
cargo run
For detailed API documentation, visit docs.rs/cfgloader_rs.