modo/config/load.rs
1use serde::de::DeserializeOwned;
2use std::path::Path;
3
4use super::env::env;
5use super::substitute::substitute_env_vars;
6use crate::error::Result;
7
8/// Loads and deserializes a YAML config file for the current environment.
9///
10/// The file is resolved as `{config_dir}/{APP_ENV}.yaml`. After reading, all
11/// `${VAR}` and `${VAR:default}` placeholders are replaced with values from
12/// the process environment before deserialization.
13///
14/// # Errors
15///
16/// Returns an error when:
17/// - The config file cannot be read.
18/// - A required `${VAR}` placeholder references an unset environment variable.
19/// - The YAML cannot be deserialized into `T`.
20///
21/// # Example
22///
23/// ```no_run
24/// use modo::config::load;
25/// use modo::Config;
26///
27/// let config: Config = load("config/").unwrap();
28/// ```
29pub fn load<T: DeserializeOwned>(config_dir: &str) -> Result<T> {
30 let environment = env();
31 let file_path = Path::new(config_dir).join(format!("{environment}.yaml"));
32
33 let raw = std::fs::read_to_string(&file_path).map_err(|e| {
34 crate::error::Error::internal(format!(
35 "failed to read config file '{}': {e}",
36 file_path.display()
37 ))
38 })?;
39
40 let substituted = substitute_env_vars(&raw)?;
41 let config: T = serde_yaml_ng::from_str(&substituted)?;
42 Ok(config)
43}