1#![allow(clippy::must_use_candidate)]
10
11use std::path::PathBuf;
12
13use crate::os::dirs as platform;
14
15pub fn hm_config_dir() -> Option<PathBuf> {
17 platform::config_dir().map(|c| c.join("hm"))
18}
19
20pub fn hm_cache_dir() -> Option<PathBuf> {
22 platform::cache_dir().map(|c| c.join("hm"))
23}
24
25pub fn hm_workspace_cache_dir() -> Option<PathBuf> {
27 hm_cache_dir().map(|c| c.join("workspaces"))
28}
29
30pub fn find_project_root(start: &std::path::Path) -> Option<PathBuf> {
34 let mut current = start;
35 loop {
36 if current.join(".hm").is_dir() {
37 return Some(current.to_path_buf());
38 }
39 current = current.parent()?;
40 }
41}
42
43#[cfg(test)]
44#[allow(clippy::unwrap_used)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn hm_config_dir_under_config() {
50 let p = hm_config_dir().unwrap();
51 assert!(p.ends_with("hm"), "expected path ending in 'hm', got {p:?}");
52 let parent = p.parent().unwrap();
53 assert!(
54 parent.ends_with(".config") || parent.ends_with("AppData/Roaming"),
55 "unexpected parent: {parent:?}"
56 );
57 }
58
59 #[test]
60 fn hm_cache_dir_under_cache() {
61 let p = hm_cache_dir().unwrap();
62 assert!(p.ends_with("hm"), "expected path ending in 'hm', got {p:?}");
63 }
64
65 #[test]
66 fn hm_workspace_cache_dir_resolves() {
67 let p = hm_workspace_cache_dir().unwrap();
68 assert!(p.ends_with("hm/workspaces"), "got {p:?}");
69 }
70
71 #[test]
72 fn find_project_root_at_current_dir() {
73 let tmp = tempfile::tempdir().unwrap();
74 std::fs::create_dir(tmp.path().join(".hm")).unwrap();
75 let found = find_project_root(tmp.path());
76 assert_eq!(found, Some(tmp.path().to_path_buf()));
77 }
78
79 #[test]
80 fn find_project_root_walks_up() {
81 let tmp = tempfile::tempdir().unwrap();
82 std::fs::create_dir(tmp.path().join(".hm")).unwrap();
83 let nested = tmp.path().join("src").join("deep");
84 std::fs::create_dir_all(&nested).unwrap();
85 let found = find_project_root(&nested);
86 assert_eq!(found, Some(tmp.path().to_path_buf()));
87 }
88
89 #[test]
90 fn find_project_root_returns_none_when_missing() {
91 let tmp = tempfile::tempdir().unwrap();
92 let found = find_project_root(tmp.path());
93 assert_eq!(found, None);
94 }
95}