use std::path::PathBuf;
#[cfg(unix)]
mod unix;
#[cfg(unix)]
pub use unix::UnixKnownPaths;
#[cfg(unix)]
pub type ActiveKnownPaths = UnixKnownPaths;
#[cfg(windows)]
mod windows;
#[cfg(windows)]
pub use windows::WindowsKnownPaths;
#[cfg(windows)]
pub type ActiveKnownPaths = WindowsKnownPaths;
pub trait KnownPaths: crate::sealed::Sealed {
fn home(&self) -> Option<PathBuf>;
fn config(&self) -> Option<PathBuf>;
fn data(&self) -> Option<PathBuf>;
fn runtime(&self) -> Option<PathBuf>;
}
pub(crate) fn env_path(name: &str) -> Option<PathBuf> {
std::env::var_os(name)
.filter(|v| !v.is_empty())
.map(PathBuf::from)
}
#[cfg(test)]
pub(crate) fn assert_known_paths_contract<P: KnownPaths>(paths: &P) {
for (what, dir) in [
("home", paths.home()),
("config", paths.config()),
("data", paths.data()),
] {
if let Some(dir) = dir {
assert!(
dir.is_absolute(),
"{what}() must be absolute or None, never a cwd-relative path: {dir:?}"
);
}
}
if let Some(data) = paths.data() {
assert!(data.is_absolute(), "data() must be absolute, got {data:?}");
}
if let (Some(c), Some(d)) = (paths.config(), paths.data()) {
assert_ne!(c, d, "config() and data() must not collide");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn active_adapter_upholds_the_contract() {
assert_known_paths_contract(&crate::KNOWN_PATHS);
}
#[test]
fn an_explicit_xdg_var_wins_on_every_platform() {
let _env = crate::ENV_GUARD.lock().unwrap_or_else(|e| e.into_inner());
let key = "XDG_DATA_HOME";
let restore = std::env::var_os(key);
let want = if cfg!(windows) { r"C:\xdg" } else { "/xdg" };
unsafe { std::env::set_var(key, want) };
let got = crate::KNOWN_PATHS.data();
unsafe {
match restore {
Some(v) => std::env::set_var(key, v),
None => std::env::remove_var(key),
}
}
assert_eq!(got, Some(PathBuf::from(want).join("hotl")));
}
}