derive_from_env

Extract type-safe structured configuration from environment variables using procedural derive macros.
All types are parsed using the FromStr trait, making it easy to use with any type that implements it - including your own custom types.
Installation
[dependencies]
derive_from_env = "0.1"
Quick Start
use derive_from_env::FromEnv;
#[derive(FromEnv)]
struct Config {
host: String,
port: u16,
debug: bool,
}
fn main() {
let config = Config::from_env().unwrap();
}
Struct Attributes
prefix = "PREFIX_"
Sets a prefix for all environment variables in the struct.
#[derive(FromEnv)]
#[from_env(prefix = "MYAPP_")]
struct Config {
host: String, port: u16, }
Field Attributes
default = "value"
Provides a fallback value when the environment variable is not set.
#[derive(FromEnv)]
struct Config {
#[from_env(default = "localhost")]
host: String,
#[from_env(default = "8080")]
port: u16,
}
var = "NAME"
Overrides the environment variable name completely. This ignores any prefix.
#[derive(FromEnv)]
#[from_env(prefix = "APP_")]
struct Config {
#[from_env(var = "DATABASE_URL")] db_url: String,
}
rename = "name"
Renames the field for environment variable lookup, but still respects the prefix.
#[derive(FromEnv)]
#[from_env(prefix = "APP_")]
struct Config {
#[from_env(rename = "db_url")] database_url: String,
}
flatten
Marks a field as a nested struct. Required for any field that is itself a struct deriving FromEnv but not implementing FromStr.
#[derive(FromEnv)]
struct DatabaseConfig {
host: String,
port: u16,
}
#[derive(FromEnv)]
struct Config {
#[from_env(flatten)]
database: DatabaseConfig, }
Struct prefixes combine when nesting structs:
#[derive(FromEnv)]
#[from_env(prefix = "SERVICE_")]
struct ServiceConfig {
host: String,
port: u16,
}
#[derive(FromEnv)]
#[from_env(prefix = "APP_")]
struct Config {
#[from_env(flatten)]
database: DatabaseConfig, }
no_prefix
Used with flatten to prevent adding the field name to the prefix chain.
#[derive(FromEnv)]
#[from_env(prefix = "APP_")]
struct Config {
#[from_env(flatten, no_prefix)]
database: DatabaseConfig, }
Type Handling
FromStr Types
All types are parsed using the FromStr trait. This includes:
- Primitives:
i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, bool, char
- Standard library types:
String, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, PathBuf
- Any custom type implementing
FromStr
use std::str::FromStr;
#[derive(Debug, PartialEq)]
enum LogLevel {
Debug,
Info,
Warn,
Error,
}
impl FromStr for LogLevel {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"debug" => Ok(LogLevel::Debug),
"info" => Ok(LogLevel::Info),
"warn" => Ok(LogLevel::Warn),
"error" => Ok(LogLevel::Error),
_ => Err(format!("unknown log level: {}", s)),
}
}
}
#[derive(FromEnv)]
struct Config {
log_level: LogLevel, }
Option Types
Option<T> fields return None when the environment variable is not set.
#[derive(FromEnv)]
struct Config {
host: String, port: Option<u16>, }
Error Handling
The from_env() method returns Result<Self, FromEnvError>.
use derive_from_env::{FromEnv, FromEnvError};
#[derive(FromEnv)]
struct Config {
host: String,
port: u16,
}
fn main() {
match Config::from_env() {
Ok(config) => println!("Loaded config: {:?}", config),
Err(FromEnvError::MissingEnvVar { var_name }) => {
eprintln!("Missing required variable: {}", var_name);
}
Err(FromEnvError::ParsingFailure { var_name, expected_type }) => {
eprintln!("Failed to parse {} as {}", var_name, expected_type);
}
}
}
License
MIT