1use anyhow::Result;
11use std::path::PathBuf;
12
13pub struct SessionManager;
15
16impl SessionManager {
17 pub fn find_project_root() -> Result<PathBuf> {
39 let mut current = std::env::current_dir()?;
40
41 loop {
42 if current.join(".patina").exists() {
43 return Ok(current);
44 }
45
46 if let Some(parent) = current.parent() {
47 current = parent.to_path_buf();
48 } else {
49 anyhow::bail!("Not in a Patina project directory");
50 }
51 }
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use std::fs;
59 use tempfile::TempDir;
60
61 #[test]
62 fn test_find_project_root_from_subdirectory() {
63 let temp_dir = TempDir::new().unwrap();
65 let patina_dir = temp_dir.path().join(".patina");
66 fs::create_dir(&patina_dir).unwrap();
67
68 let sub_dir = temp_dir.path().join("src").join("commands");
70 fs::create_dir_all(&sub_dir).unwrap();
71
72 struct DirGuard {
74 original: Option<PathBuf>,
75 }
76 impl Drop for DirGuard {
77 fn drop(&mut self) {
78 if let Some(ref path) = self.original {
79 let _ = std::env::set_current_dir(path);
80 }
81 }
82 }
83
84 let _guard = DirGuard {
85 original: std::env::current_dir().ok(),
86 };
87
88 std::env::set_current_dir(&sub_dir).unwrap();
89
90 let found_root = SessionManager::find_project_root().unwrap();
92 assert_eq!(
93 found_root.canonicalize().unwrap(),
94 temp_dir.path().canonicalize().unwrap()
95 );
96 }
97
98 #[test]
99 fn test_find_project_root_not_in_project() {
100 let temp_dir = TempDir::new().unwrap();
102
103 struct DirGuard {
105 original: Option<PathBuf>,
106 }
107 impl Drop for DirGuard {
108 fn drop(&mut self) {
109 if let Some(ref path) = self.original {
110 let _ = std::env::set_current_dir(path);
111 }
112 }
113 }
114
115 let _guard = DirGuard {
116 original: std::env::current_dir().ok(),
117 };
118
119 std::env::set_current_dir(temp_dir.path()).unwrap();
120
121 let result = SessionManager::find_project_root();
123 assert!(result.is_err());
124 assert!(result
125 .unwrap_err()
126 .to_string()
127 .contains("Not in a Patina project directory"));
128 }
129
130 #[test]
131 fn test_find_project_root_at_root() {
132 let temp_dir = TempDir::new().unwrap();
134 let patina_dir = temp_dir.path().join(".patina");
135 fs::create_dir(&patina_dir).unwrap();
136
137 struct DirGuard {
139 original: Option<PathBuf>,
140 }
141 impl Drop for DirGuard {
142 fn drop(&mut self) {
143 if let Some(ref path) = self.original {
144 let _ = std::env::set_current_dir(path);
145 }
146 }
147 }
148
149 let _guard = DirGuard {
150 original: std::env::current_dir().ok(),
151 };
152
153 std::env::set_current_dir(temp_dir.path()).unwrap();
154
155 let found_root = SessionManager::find_project_root().unwrap();
157 assert_eq!(
158 found_root.canonicalize().unwrap(),
159 temp_dir.path().canonicalize().unwrap()
160 );
161 }
162}