1use serde::Deserialize;
2
3pub fn read_config<P, T>(path: P) -> Result<T, ConfigError>
4where
5 P: AsRef<std::path::Path>,
6 for<'de> T: Deserialize<'de>,
7{
8 let data = std::fs::read_to_string(path)?;
9 let re = regex::Regex::new(r"\$\{([a-zA-Z_][0-9a-zA-Z_]*)\}").unwrap();
10 let result = re.replace_all(&data, |caps: ®ex::Captures| {
11 match std::env::var(&caps[1]) {
12 Ok(value) => value,
13 Err(_) => {
14 eprintln!("WARN: Environment variable {} was not set", &caps[1]);
15 String::default()
16 }
17 }
18 });
19
20 config::Config::builder()
21 .add_source(config::File::from_str(
22 result.as_ref(),
23 config::FileFormat::Yaml,
24 ))
25 .build()
26 .map_err(ConfigError::BuildError)?
27 .try_deserialize()
28 .map_err(ConfigError::ParseError)
29}
30
31#[derive(thiserror::Error, Debug)]
32pub enum ConfigError {
33 #[error("failed to read config")]
34 UnableToRead(#[from] std::io::Error),
35 #[error("failed to build config")]
36 BuildError(#[source] config::ConfigError),
37 #[error("failed to parse config")]
38 ParseError(#[source] config::ConfigError),
39}