Skip to main content

rs_zero/core/
config.rs

1use serde::de::DeserializeOwned;
2
3/// Loads configuration from `basename` and environment variables with the given prefix.
4///
5/// The file source follows the `config` crate lookup rules, so `etc/app`
6/// will resolve to formats such as `etc/app.toml`. Environment variables
7/// override file values and use `__` as nested separator.
8pub fn load_config<T>(basename: &str, env_prefix: &str) -> Result<T, config::ConfigError>
9where
10    T: DeserializeOwned,
11{
12    config::Config::builder()
13        .add_source(config::File::with_name(basename).required(false))
14        .add_source(
15            config::Environment::with_prefix(env_prefix)
16                .separator("__")
17                .try_parsing(true),
18        )
19        .build()?
20        .try_deserialize::<T>()
21}
22
23#[cfg(test)]
24mod tests {
25    use super::load_config;
26    use serde::Deserialize;
27    use std::io::Write;
28
29    #[derive(Debug, Deserialize)]
30    struct App {
31        server: Server,
32    }
33
34    #[derive(Debug, Deserialize)]
35    struct Server {
36        port: u16,
37    }
38
39    #[test]
40    fn loads_toml_file() {
41        let dir = tempfile::tempdir().expect("tempdir");
42        let file_path = dir.path().join("app.toml");
43        let mut file = std::fs::File::create(&file_path).expect("create config");
44        writeln!(file, "[server]\nport = 8080").expect("write config");
45
46        let basename = file_path.with_extension("");
47        let app: App =
48            load_config(basename.to_str().expect("utf8 path"), "RSZERO_TEST").expect("load config");
49
50        assert_eq!(app.server.port, 8080);
51    }
52}