Figment Layered Environment Config

Compose application configuration with figment layers, joining TOML files with environment overrides and strict extraction.

RustConfig Management

Code

snippet
use figment::{
    providers::{Env, Format, Serialized, Toml},
    Figment,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;

#[derive(Debug, Serialize, Deserialize, Clone)]
struct DatabaseConfig {
    url: String,
    pool_size: u32,
    connect_timeout_ms: u64,
}

#[derive(Debug, Serialize, Deserialize)]
struct AppConfig {
    environment: String,
    port: u16,
    database: DatabaseConfig,
}

fn layered_config() -> Result<AppConfig, figment::Error> {
    let defaults = AppConfig {
        environment: "development".into(),
        port: 3000,
        database: DatabaseConfig {
            url: "postgres://localhost/app".into(),
            pool_size: 8,
            connect_timeout_ms: 3000,
        },
    };

    Figment::from(Serialized::defaults(defaults)) // 内置默认值打底
        .merge(Toml::file("config/app.toml")) // 配置文件覆盖
        .merge(Env::prefixed("APP_").split("__")) // 环境变量压顶
        .extract()
}

fn main() {
    // APP_DATABASE__URL 等:双下划线映射嵌套键
    std::env::set_var("APP_ENVIRONMENT", "production");
    std::env::set_var("APP_DATABASE__POOL_SIZE", "24");

    match layered_config() {
        Ok(config) => {
            println!(
                "{} on port {} -> {} (pool {})",
                config.environment,
                config.port,
                config.database.url,
                config.database.pool_size
            );
            let timeout = Duration::from_millis(config.database.connect_timeout_ms);
            println!("connect timeout: {timeout:?}");
        }
        Err(err) => {
            // extract 失败会报告具体哪个字段在哪个层出了问题
            eprintln!("config error: {err}");
        }
    }
}