Skip to main content

ferrox_config/
lib.rs

1use ferrox_errors::AppError;
2use figment::{
3    providers::{Env, Format, Toml},
4    Figment,
5};
6use serde::Deserialize;
7use tracing::info;
8
9/// Loads and validates configuration from `default.toml`, environment-specific `[env].toml`, 
10/// and environment variables. Uses strongly typed deserialization to Fail-Fast on missing keys.
11pub fn load_config<'a, T: Deserialize<'a>>(env_prefix: &str) -> Result<T, AppError> {
12    let environment = std::env::var("FERROX_ENV").unwrap_or_else(|_| "development".into());
13
14    let config: T = Figment::new()
15        .merge(Toml::file("config/default.toml"))
16        .merge(Toml::file(format!("config/{}.toml", environment)))
17        .merge(Env::prefixed(env_prefix))
18        .extract()
19        .map_err(|e| AppError::InternalServerError(Box::new(e)))?;
20
21    info!("Loaded strongly-typed configuration for environment: {}", environment);
22
23    Ok(config)
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29    use serde::Deserialize;
30
31    #[derive(Deserialize, Debug, PartialEq)]
32    struct TestConfig {
33        host: String,
34        port: u16,
35    }
36
37    #[test]
38    fn test_load_config_env_override() {
39        // Set an env var that should be picked up by Figment
40        std::env::set_var("APP_HOST", "127.0.0.1");
41        std::env::set_var("APP_PORT", "8080");
42
43        let config: Result<TestConfig, _> = load_config("APP_");
44        assert!(config.is_ok());
45        
46        let config = config.unwrap();
47        assert_eq!(config.host, "127.0.0.1");
48        assert_eq!(config.port, 8080);
49    }
50}