1use std::path::{Path, PathBuf};
2
3use crate::error::ZigError;
4
5pub fn global_workflows_dir_from(home: &Path) -> PathBuf {
7 home.join(".zig").join("workflows")
8}
9
10pub fn global_workflows_dir() -> Option<PathBuf> {
13 std::env::var("HOME")
14 .ok()
15 .map(|home| global_workflows_dir_from(Path::new(&home)))
16}
17
18pub fn ensure_global_workflows_dir() -> Result<PathBuf, ZigError> {
20 let dir = global_workflows_dir()
21 .ok_or_else(|| ZigError::Io("HOME environment variable not set".into()))?;
22 if !dir.exists() {
23 std::fs::create_dir_all(&dir)
24 .map_err(|e| ZigError::Io(format!("failed to create {}: {e}", dir.display())))?;
25 }
26 Ok(dir)
27}
28
29pub fn cwd_workflows_dir_from(start: &Path) -> Option<PathBuf> {
41 let mut current = start;
42 let stop = find_git_root(start);
43
44 loop {
45 let candidate = current.join(".zig").join("workflows");
46 if candidate.is_dir() {
47 return Some(candidate);
48 }
49 if let Some(ref root) = stop {
50 if current == root.as_path() {
51 return None;
52 }
53 }
54 match current.parent() {
55 Some(p) => current = p,
56 None => return None,
57 }
58 }
59}
60
61pub fn cwd_workflows_dir() -> Option<PathBuf> {
64 let cwd = std::env::current_dir().ok()?;
65 cwd_workflows_dir_from(&cwd)
66}
67
68pub fn global_resources_dir_from(home: &Path) -> PathBuf {
85 home.join(".zig").join("resources")
86}
87
88pub fn global_resources_dir() -> Option<PathBuf> {
91 std::env::var("HOME")
92 .ok()
93 .map(|h| global_resources_dir_from(Path::new(&h)))
94}
95
96pub fn global_resources_for(workflow: &str) -> Option<PathBuf> {
98 global_resources_dir().map(|d| d.join(workflow))
99}
100
101pub fn global_shared_resources_dir() -> Option<PathBuf> {
105 global_resources_dir().map(|d| d.join("_shared"))
106}
107
108pub fn ensure_global_resources_dir(child: Option<&str>) -> Result<PathBuf, ZigError> {
110 let mut dir = global_resources_dir()
111 .ok_or_else(|| ZigError::Io("HOME environment variable not set".into()))?;
112 if let Some(c) = child {
113 dir = dir.join(c);
114 }
115 if !dir.exists() {
116 std::fs::create_dir_all(&dir)
117 .map_err(|e| ZigError::Io(format!("failed to create {}: {e}", dir.display())))?;
118 }
119 Ok(dir)
120}
121
122pub fn cwd_resources_dir_from(start: &Path) -> Option<PathBuf> {
129 let mut current = start;
130 let stop = find_git_root(start);
131
132 loop {
133 let candidate = current.join(".zig").join("resources");
134 if candidate.is_dir() {
135 return Some(candidate);
136 }
137 if let Some(ref root) = stop {
138 if current == root.as_path() {
139 return None;
140 }
141 }
142 match current.parent() {
143 Some(p) => current = p,
144 None => return None,
145 }
146 }
147}
148
149pub fn cwd_resources_dir() -> Option<PathBuf> {
152 let cwd = std::env::current_dir().ok()?;
153 cwd_resources_dir_from(&cwd)
154}
155
156pub fn global_memory_dir() -> Option<PathBuf> {
162 std::env::var("HOME")
163 .ok()
164 .map(|h| Path::new(&h).join(".zig").join("memory"))
165}
166
167pub fn global_memory_for(workflow: &str) -> Option<PathBuf> {
169 global_memory_dir().map(|d| d.join(workflow))
170}
171
172pub fn global_shared_memory_dir() -> Option<PathBuf> {
174 global_memory_dir().map(|d| d.join("_shared"))
175}
176
177pub fn ensure_global_memory_dir(child: Option<&str>) -> Result<PathBuf, ZigError> {
179 let mut dir = global_memory_dir()
180 .ok_or_else(|| ZigError::Io("HOME environment variable not set".into()))?;
181 if let Some(c) = child {
182 dir = dir.join(c);
183 }
184 if !dir.exists() {
185 std::fs::create_dir_all(&dir)
186 .map_err(|e| ZigError::Io(format!("failed to create {}: {e}", dir.display())))?;
187 }
188 Ok(dir)
189}
190
191pub fn cwd_memory_dir_from(start: &Path) -> Option<PathBuf> {
195 let mut current = start;
196 let stop = find_git_root(start);
197
198 loop {
199 let candidate = current.join(".zig").join("memory");
200 if candidate.is_dir() {
201 return Some(candidate);
202 }
203 if let Some(ref root) = stop {
204 if current == root.as_path() {
205 return None;
206 }
207 }
208 match current.parent() {
209 Some(p) => current = p,
210 None => return None,
211 }
212 }
213}
214
215pub fn cwd_memory_dir() -> Option<PathBuf> {
218 let cwd = std::env::current_dir().ok()?;
219 cwd_memory_dir_from(&cwd)
220}
221
222pub fn global_examples_dir() -> Option<PathBuf> {
228 global_base_dir().map(|d| d.join("examples"))
229}
230
231pub fn ensure_global_examples_dir() -> Result<PathBuf, ZigError> {
233 let dir = global_examples_dir()
234 .ok_or_else(|| ZigError::Io("HOME environment variable not set".into()))?;
235 if !dir.exists() {
236 std::fs::create_dir_all(&dir)
237 .map_err(|e| ZigError::Io(format!("failed to create {}: {e}", dir.display())))?;
238 }
239 Ok(dir)
240}
241
242pub fn global_base_dir() -> Option<PathBuf> {
260 std::env::var("HOME")
261 .ok()
262 .map(|h| Path::new(&h).join(".zig"))
263}
264
265pub fn sanitize_project_path(path: &str) -> String {
270 path.trim_start_matches('/').replace('/', "-")
271}
272
273fn find_git_root(start: &Path) -> Option<PathBuf> {
275 let mut current = start;
276 loop {
277 if current.join(".git").exists() {
278 return Some(current.to_path_buf());
279 }
280 current = current.parent()?;
281 }
282}
283
284pub fn project_dir(root: Option<&str>) -> Option<PathBuf> {
291 let base = global_base_dir()?;
292 if let Some(r) = root {
293 return Some(base.join("projects").join(sanitize_project_path(r)));
294 }
295 let cwd = std::env::current_dir().ok()?;
296 if let Some(git_root) = find_git_root(&cwd) {
297 let sanitized = sanitize_project_path(&git_root.to_string_lossy());
298 return Some(base.join("projects").join(sanitized));
299 }
300 Some(base)
301}
302
303pub fn project_logs_dir(root: Option<&str>) -> Option<PathBuf> {
305 project_dir(root).map(|p| p.join("logs"))
306}
307
308pub fn project_sessions_dir(root: Option<&str>) -> Option<PathBuf> {
310 project_logs_dir(root).map(|p| p.join("sessions"))
311}
312
313pub fn project_index_path(root: Option<&str>) -> Option<PathBuf> {
315 project_logs_dir(root).map(|p| p.join("index.json"))
316}
317
318pub fn global_sessions_index_path() -> Option<PathBuf> {
320 global_base_dir().map(|p| p.join("sessions_index.json"))
321}
322
323pub fn ensure_project_sessions_dir(root: Option<&str>) -> Result<PathBuf, ZigError> {
325 let dir = project_sessions_dir(root)
326 .ok_or_else(|| ZigError::Io("HOME environment variable not set".into()))?;
327 if !dir.exists() {
328 std::fs::create_dir_all(&dir)
329 .map_err(|e| ZigError::Io(format!("failed to create {}: {e}", dir.display())))?;
330 }
331 Ok(dir)
332}
333
334#[cfg(test)]
335#[path = "paths_tests.rs"]
336mod tests;