Merge multiple providers with figment
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(())
});