use super::DirectoryFinder;
use crate::env::Env;
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct UnixDirectoryFinder {
app_name: String,
legacy_rc: bool,
}
impl UnixDirectoryFinder {
pub fn new(app_name: impl Into<String>) -> Self {
Self {
app_name: app_name.into(),
legacy_rc: true,
}
}
#[must_use]
pub const fn legacy_rc(mut self, enabled: bool) -> Self {
self.legacy_rc = enabled;
self
}
fn config_home(env: &dyn Env) -> PathBuf {
env.get("XDG_CONFIG_HOME")
.map(PathBuf::from)
.or_else(|| env.home_dir().map(|h| h.join(".config")))
.unwrap_or_else(|| PathBuf::from("/").join(".config"))
}
fn xdg_config_dirs(env: &dyn Env) -> Vec<PathBuf> {
env.get("XDG_CONFIG_DIRS").map_or_else(
|| vec![PathBuf::from("/etc/xdg")],
|dirs| dirs.split(':').map(PathBuf::from).collect(),
)
}
}
impl DirectoryFinder for UnixDirectoryFinder {
fn user_dirs(&self, env: &dyn Env) -> Vec<PathBuf> {
let mut paths = Vec::new();
paths.push(Self::config_home(env).join(&self.app_name));
if self.legacy_rc
&& let Some(home) = env.home_dir()
{
let rc_name = format!(".{}rc", self.app_name);
paths.push(home.join(rc_name));
}
paths
}
fn local_dirs(&self, _env: &dyn Env) -> Vec<PathBuf> {
vec![
PathBuf::from("/usr/local/etc").join(&self.app_name),
PathBuf::from("/opt").join(&self.app_name).join("etc"),
]
}
fn system_dirs(&self, env: &dyn Env) -> Vec<PathBuf> {
let mut paths = Vec::new();
for dir in Self::xdg_config_dirs(env) {
paths.push(dir.join(&self.app_name));
}
paths.push(PathBuf::from("/etc").join(&self.app_name));
paths
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestEnv {
vars: std::collections::HashMap<String, String>,
home: PathBuf,
}
impl TestEnv {
fn new() -> Self {
Self {
vars: std::collections::HashMap::new(),
home: PathBuf::from("/home/user"),
}
}
fn with_var(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.vars.insert(key.into(), value.into());
self
}
}
impl Env for TestEnv {
fn get(&self, key: &str) -> Option<String> {
self.vars.get(key).cloned()
}
fn home_dir(&self) -> Option<PathBuf> {
Some(self.home.clone())
}
}
#[test]
fn test_user_dirs_with_xdg() {
let env = TestEnv::new().with_var("XDG_CONFIG_HOME", "/custom/config");
let finder = UnixDirectoryFinder::new("myapp");
let dirs = finder.user_dirs(&env);
assert_eq!(
dirs,
vec![
PathBuf::from("/custom/config/myapp"),
PathBuf::from("/home/user/.myapprc"),
]
);
}
#[test]
fn test_user_dirs_fallback() {
let env = TestEnv::new();
let finder = UnixDirectoryFinder::new("myapp");
let dirs = finder.user_dirs(&env);
assert_eq!(dirs[0], PathBuf::from("/home/user/.config/myapp"));
}
#[test]
fn test_no_legacy() {
let env = TestEnv::new();
let finder = UnixDirectoryFinder::new("myapp").legacy_rc(false);
let dirs = finder.user_dirs(&env);
assert_eq!(dirs.len(), 1);
assert_eq!(dirs[0], PathBuf::from("/home/user/.config/myapp"));
}
#[test]
fn test_system_dirs() {
let env = TestEnv::new().with_var("XDG_CONFIG_DIRS", "/etc/xdg:/opt/config");
let finder = UnixDirectoryFinder::new("myapp");
let dirs = finder.system_dirs(&env);
assert_eq!(
dirs,
vec![
PathBuf::from("/etc/xdg/myapp"),
PathBuf::from("/opt/config/myapp"),
PathBuf::from("/etc/myapp"),
]
);
}
#[test]
fn test_local_dirs() {
let env = TestEnv::new();
let finder = UnixDirectoryFinder::new("myapp");
let dirs = finder.local_dirs(&env);
assert_eq!(
dirs,
vec![
PathBuf::from("/usr/local/etc/myapp"),
PathBuf::from("/opt/myapp/etc"),
]
);
}
}