use anyhow::Context;
use config::{Config, Environment, File};
use serde::{Deserialize, de::DeserializeOwned};
use std::{
env,
path::{Path, PathBuf},
};
pub fn take_from_args(pos: usize) -> Option<String> {
env::args().nth(pos)
}
pub fn load_cfg<T: for<'a> Deserialize<'a>>(path: impl AsRef<Path>) -> anyhow::Result<T> {
let pb = path.as_ref().to_path_buf();
if !pb.exists() {
return Err(anyhow::anyhow!("file {} does not exist", pb.display()));
}
let cfg = Config::builder()
.add_source(config::File::from(PathBuf::from(path.as_ref())))
.build()
.with_context(|| format!("failed to read config from {}", pb.display()))?;
let des: T = cfg
.try_deserialize()
.with_context(|| format!("failed to deserialize config from {}", pb.display()))?;
Ok(des)
}
pub fn load_cfg_merge<T, P>(
paths: impl IntoIterator<Item = P>,
env_prefix: Option<&str>,
) -> anyhow::Result<T>
where
T: DeserializeOwned,
P: AsRef<Path>,
{
let mut builder = Config::builder();
for p in paths {
let pb = p.as_ref().to_path_buf();
if pb.exists() {
builder = builder.add_source(File::from(pb));
} else {
println!("config loading: file {} does not exist", pb.display());
}
}
builder = match env_prefix {
Some(prefix) => builder.add_source(Environment::with_prefix(prefix).separator("__")),
None => builder.add_source(Environment::default().separator("__")),
};
let cfg = builder
.build()
.with_context(|| "failed to build configuration from provided sources")?;
let des: T = cfg
.try_deserialize()
.with_context(|| "failed to deserialize merged configuration")?;
Ok(des)
}
pub fn load_cfg_from_dir<T, D, F>(
dir: D,
files: impl IntoIterator<Item = F>,
env_prefix: Option<&str>,
) -> anyhow::Result<T>
where
T: DeserializeOwned,
D: AsRef<Path>,
F: AsRef<Path>,
{
let dir_ref = dir.as_ref();
let mut builder = Config::builder();
for fname in files {
let path = dir_ref.join(fname.as_ref());
if path.exists() {
builder = builder.add_source(File::from(path));
} else {
println!(
"config loading: file {} does not exist (dir: {}) — skipping",
path.display(),
dir_ref.display()
);
}
}
builder = match env_prefix {
Some(prefix) => builder.add_source(Environment::with_prefix(prefix).separator("__")),
None => builder.add_source(Environment::default().separator("__")),
};
let cfg = builder
.build()
.with_context(|| format!("failed to build configuration from dir {:?}", dir_ref))?;
let des: T = cfg
.try_deserialize()
.with_context(|| "failed to deserialize merged configuration")?;
Ok(des)
}