use crate::attrs::extract_app_name;
use crate::error::HierConfError;
use camino::Utf8PathBuf;
use facet::Facet;
#[cfg(feature = "user-config")]
use std::env;
use std::fs;
pub fn config_paths<T>() -> Result<Vec<(Option<Utf8PathBuf>, String)>, HierConfError>
where
T: Facet<'static>,
{
let app_name = extract_app_name::<T>()?;
let mut paths = Vec::new();
let mut add_config_path = |base: Option<Utf8PathBuf>, display: &str| {
let path = base.map(|b| b.join(app_name).join("config.toml"));
let display_path = format!("{}/{}/config.toml", display, app_name);
paths.push((path, display_path));
};
#[cfg(feature = "test-paths")]
{
let manifest_dir = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let test_path_opt = manifest_dir
.parent() .map(|p| p.join("hierconf").join("tests"));
add_config_path(test_path_opt, "tests");
}
#[cfg(feature = "user-config")]
{
#[cfg(target_os = "macos")]
{
let config_home = env::var("HOME")
.ok()
.map(|home| Utf8PathBuf::from(home).join("Library/Application Support"));
add_config_path(config_home, "${HOME}/Library/Application Support");
}
#[cfg(not(target_os = "macos"))]
{
let config_home = env::var("XDG_CONFIG_HOME")
.ok()
.map(Utf8PathBuf::from)
.or_else(|| {
env::var("HOME")
.ok()
.map(|home| Utf8PathBuf::from(home).join(".config"))
});
add_config_path(config_home, "${XDG_CONFIG_HOME:-${HOME}/.config}");
}
}
add_config_path(Some(Utf8PathBuf::from("/etc")), "/etc");
if let Some(distribution_prefix) = option_env!("HIERCONF_DISTRIBUTION_PREFIX") {
add_config_path(
Some(Utf8PathBuf::from(distribution_prefix)),
distribution_prefix,
);
}
add_config_path(Some(Utf8PathBuf::from("/usr/share/etc")), "/usr/share/etc");
Ok(paths)
}
pub fn load_config<T>() -> Result<T, HierConfError>
where
T: Facet<'static>,
{
let paths = config_paths::<T>()?;
for (path_opt, _display) in &paths {
if let Some(path) = path_opt
&& path.exists()
&& path.is_file()
{
let content = fs::read_to_string(path)?;
return Ok(facet_toml::from_str(&content)?);
}
}
let locations: Vec<Utf8PathBuf> = paths
.into_iter()
.filter_map(|(path_opt, _display)| path_opt)
.collect();
Err(HierConfError::NoConfigFiles { locations })
}