use std::path::PathBuf;
pub const CONFIG_FILE_NAME: &str = "config.toml";
fn home_env_var(tool: &str) -> String {
format!("{}_HOME", tool.to_uppercase().replace('-', "_"))
}
pub fn system_config_path(tool: &str) -> PathBuf {
#[cfg(target_os = "windows")]
let base_path = PathBuf::from("C:\\ProgramData");
#[cfg(not(target_os = "windows"))]
let base_path = PathBuf::from("/etc");
base_path.join(tool).join(CONFIG_FILE_NAME)
}
pub fn user_config_paths(tool: &str) -> Vec<PathBuf> {
[
#[cfg(target_os = "macos")]
std::env::var("XDG_CONFIG_HOME")
.ok()
.map(|d| PathBuf::from(d).join(tool).join(CONFIG_FILE_NAME)),
dirs::config_dir().map(|d| d.join(tool).join(CONFIG_FILE_NAME)),
tool_home(tool).map(|d| d.join(CONFIG_FILE_NAME)),
]
.into_iter()
.flatten()
.collect()
}
pub fn tool_home(tool: &str) -> Option<PathBuf> {
if let Some(path) = std::env::var_os(home_env_var(tool)) {
Some(PathBuf::from(path))
} else {
dirs::home_dir().map(|home| home.join(format!(".{tool}")))
}
}
pub fn config_search_paths(tools: &[&str]) -> Vec<PathBuf> {
let mut paths: Vec<PathBuf> = tools
.iter()
.map(|tool| system_config_path(tool))
.chain(tools.iter().flat_map(|tool| user_config_paths(tool)))
.collect();
let mut seen = std::collections::HashSet::new();
let mut deduped: Vec<PathBuf> = paths
.drain(..)
.rev()
.filter(|path| seen.insert(path.clone()))
.collect();
deduped.reverse();
deduped
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn home_env_var_is_derived_from_tool_name() {
assert_eq!(home_env_var("pixi"), "PIXI_HOME");
assert_eq!(home_env_var("rattler-build"), "RATTLER_BUILD_HOME");
}
#[test]
fn search_paths_order_system_before_user() {
let paths = config_search_paths(&["pixi", "rattler-build"]);
let system_pixi = system_config_path("pixi");
let user_pixi = user_config_paths("pixi");
let system_pos = paths.iter().position(|p| p == &system_pixi);
let user_pos = user_pixi
.first()
.and_then(|first| paths.iter().position(|p| p == first));
if let (Some(system_pos), Some(user_pos)) = (system_pos, user_pos) {
assert!(
system_pos < user_pos,
"system config must have lower precedence than user config"
);
}
}
#[test]
fn search_paths_order_within_user_group_follows_tool_order() {
let paths = config_search_paths(&["pixi", "rattler-build"]);
let pixi_user = user_config_paths("pixi");
let rb_user = user_config_paths("rattler-build");
if let (Some(pixi_first), Some(rb_last)) = (pixi_user.first(), rb_user.last()) {
let pixi_pos = paths.iter().position(|p| p == pixi_first);
let rb_pos = paths.iter().position(|p| p == rb_last);
if let (Some(pixi_pos), Some(rb_pos)) = (pixi_pos, rb_pos) {
assert!(
pixi_pos < rb_pos,
"later tools must take precedence over earlier ones"
);
}
}
}
}