use std::path::{Path, PathBuf};
fn resolve_home(oxi_home: Option<&str>, user_home: Option<&Path>) -> Option<PathBuf> {
if let Some(p) = oxi_home.filter(|s| !s.is_empty()) {
return Some(PathBuf::from(p));
}
user_home.map(|h| h.join(".oxi"))
}
pub fn home_dir() -> std::io::Result<PathBuf> {
resolve_home(
std::env::var("OXI_HOME").ok().as_deref(),
dirs::home_dir().as_deref(),
)
.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::NotFound,
"neither OXI_HOME nor HOME is set",
)
})
}
pub fn catalog_override_dir() -> Option<PathBuf> {
home_dir().ok().map(|h| h.join("catalog"))
}
pub fn cache_dir() -> Option<PathBuf> {
home_dir().ok().map(|h| h.join("cache"))
}
pub fn auth_path() -> Option<PathBuf> {
home_dir().ok().map(|h| h.join("auth.json"))
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn oxi_home_wins_when_set() {
let got = resolve_home(
Some("/custom/oxios"),
Some(PathBuf::from("/home/u").as_path()),
);
assert_eq!(got, Some(PathBuf::from("/custom/oxios")));
}
#[test]
fn empty_oxi_home_falls_through() {
let got = resolve_home(Some(""), Some(PathBuf::from("/home/u").as_path()));
assert_eq!(got, Some(PathBuf::from("/home/u/.oxi")));
}
#[test]
fn defaults_to_user_home_dot_oxi() {
let got = resolve_home(None, Some(PathBuf::from("/home/u").as_path()));
assert_eq!(got, Some(PathBuf::from("/home/u/.oxi")));
}
#[test]
fn none_when_both_absent() {
assert_eq!(resolve_home(None, None), None);
}
#[test]
fn subpaths_compose_from_resolved_home() {
let home = resolve_home(Some("/x"), None).unwrap();
assert_eq!(
home.join("catalog").join("overrides.toml"),
PathBuf::from("/x/catalog/overrides.toml")
);
assert_eq!(
home.join("cache").join("models-dev.json"),
PathBuf::from("/x/cache/models-dev.json")
);
assert_eq!(home.join("auth.json"), PathBuf::from("/x/auth.json"));
}
#[test]
fn home_dir_resolves_in_ci() {
let resolved = home_dir();
assert!(resolved.is_ok(), "expected a resolvable home dir");
}
}