Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Merge multiple providers with figment

figment-badge cat-config-badge

figment assembles configuration from independent providers. A provider can be a TOML, JSON, or YAML file, or a set of environment variables. Figment merges them into a typed value. It is the configuration library behind Rocket, and its Jail makes configuration code testable without touching the real filesystem or environment.

A Figment stacks providers with merge: later providers override keys set by earlier ones. Here Toml::file reads the base settings and Env::prefixed layers APP_-prefixed environment variables on top, so APP_PORT=9000 overrides the file’s port. extract deserializes the merged result into the Config struct.

use figment::providers::{Env, Format, Toml};
use figment::Figment;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Config {
    name: String,
    port: u16,
    workers: usize,
}

fn figment() -> Figment {
    Figment::new()
        .merge(Toml::file("App.toml"))
        .merge(Env::prefixed("APP_"))
}

fn main() -> Result<(), figment::Error> {
    let config: Config = figment().extract()?;
    println!(
        "{} listening on port {} with {} workers",
        config.name, config.port, config.workers
    );
    Ok(())
}

Configuration code is awkward to test when it reads real files and environment variables. Jail::expect_with runs a closure in a temporary directory with a sandboxed environment, so a test can create an App.toml and set variables that disappear when the closure returns:

    Jail::expect_with(|jail| {
        jail.create_file(
            "App.toml",
            r#"
                name = "api"
                port = 8000
                workers = 4
            "#,
        )?;
        jail.set_env("APP_PORT", 9000);

        let config: Config = figment().extract()?;
        assert_eq!(config.name, "api");
        assert_eq!(config.port, 9000);
        assert_eq!(config.workers, 4);
        Ok(())
    });