#![allow(clippy::must_use_candidate)]
use std::path::PathBuf;
use crate::os::dirs as platform;
pub fn hm_config_dir() -> Option<PathBuf> {
platform::config_dir().map(|c| c.join("hm"))
}
pub fn hm_cache_dir() -> Option<PathBuf> {
platform::cache_dir().map(|c| c.join("hm"))
}
pub fn hm_workspace_cache_dir() -> Option<PathBuf> {
hm_cache_dir().map(|c| c.join("workspaces"))
}
pub fn find_project_root(start: &std::path::Path) -> Option<PathBuf> {
let mut current = start;
loop {
if current.join(".hm").is_dir() {
return Some(current.to_path_buf());
}
current = current.parent()?;
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn hm_config_dir_under_config() {
let p = hm_config_dir().unwrap();
assert!(p.ends_with("hm"), "expected path ending in 'hm', got {p:?}");
let parent = p.parent().unwrap();
assert!(
parent.ends_with(".config") || parent.ends_with("AppData/Roaming"),
"unexpected parent: {parent:?}"
);
}
#[test]
fn hm_cache_dir_under_cache() {
let p = hm_cache_dir().unwrap();
assert!(p.ends_with("hm"), "expected path ending in 'hm', got {p:?}");
}
#[test]
fn hm_workspace_cache_dir_resolves() {
let p = hm_workspace_cache_dir().unwrap();
assert!(p.ends_with("hm/workspaces"), "got {p:?}");
}
#[test]
fn find_project_root_at_current_dir() {
let tmp = tempfile::tempdir().unwrap();
std::fs::create_dir(tmp.path().join(".hm")).unwrap();
let found = find_project_root(tmp.path());
assert_eq!(found, Some(tmp.path().to_path_buf()));
}
#[test]
fn find_project_root_walks_up() {
let tmp = tempfile::tempdir().unwrap();
std::fs::create_dir(tmp.path().join(".hm")).unwrap();
let nested = tmp.path().join("src").join("deep");
std::fs::create_dir_all(&nested).unwrap();
let found = find_project_root(&nested);
assert_eq!(found, Some(tmp.path().to_path_buf()));
}
#[test]
fn find_project_root_returns_none_when_missing() {
let tmp = tempfile::tempdir().unwrap();
let found = find_project_root(tmp.path());
assert_eq!(found, None);
}
}