use std::path::{Path, PathBuf};
use crate::error::ZigError;
pub fn global_workflows_dir_from(home: &Path) -> PathBuf {
home.join(".zig").join("workflows")
}
pub fn global_workflows_dir() -> Option<PathBuf> {
std::env::var("HOME")
.ok()
.map(|home| global_workflows_dir_from(Path::new(&home)))
}
pub fn ensure_global_workflows_dir() -> Result<PathBuf, ZigError> {
let dir = global_workflows_dir()
.ok_or_else(|| ZigError::Io("HOME environment variable not set".into()))?;
if !dir.exists() {
std::fs::create_dir_all(&dir)
.map_err(|e| ZigError::Io(format!("failed to create {}: {e}", dir.display())))?;
}
Ok(dir)
}
pub fn cwd_workflows_dir_from(start: &Path) -> Option<PathBuf> {
let mut current = start;
let stop = find_git_root(start);
loop {
let candidate = current.join(".zig").join("workflows");
if candidate.is_dir() {
return Some(candidate);
}
if let Some(ref root) = stop {
if current == root.as_path() {
return None;
}
}
match current.parent() {
Some(p) => current = p,
None => return None,
}
}
}
pub fn cwd_workflows_dir() -> Option<PathBuf> {
let cwd = std::env::current_dir().ok()?;
cwd_workflows_dir_from(&cwd)
}
pub fn global_resources_dir_from(home: &Path) -> PathBuf {
home.join(".zig").join("resources")
}
pub fn global_resources_dir() -> Option<PathBuf> {
std::env::var("HOME")
.ok()
.map(|h| global_resources_dir_from(Path::new(&h)))
}
pub fn global_resources_for(workflow: &str) -> Option<PathBuf> {
global_resources_dir().map(|d| d.join(workflow))
}
pub fn global_shared_resources_dir() -> Option<PathBuf> {
global_resources_dir().map(|d| d.join("_shared"))
}
pub fn ensure_global_resources_dir(child: Option<&str>) -> Result<PathBuf, ZigError> {
let mut dir = global_resources_dir()
.ok_or_else(|| ZigError::Io("HOME environment variable not set".into()))?;
if let Some(c) = child {
dir = dir.join(c);
}
if !dir.exists() {
std::fs::create_dir_all(&dir)
.map_err(|e| ZigError::Io(format!("failed to create {}: {e}", dir.display())))?;
}
Ok(dir)
}
pub fn cwd_resources_dir_from(start: &Path) -> Option<PathBuf> {
let mut current = start;
let stop = find_git_root(start);
loop {
let candidate = current.join(".zig").join("resources");
if candidate.is_dir() {
return Some(candidate);
}
if let Some(ref root) = stop {
if current == root.as_path() {
return None;
}
}
match current.parent() {
Some(p) => current = p,
None => return None,
}
}
}
pub fn cwd_resources_dir() -> Option<PathBuf> {
let cwd = std::env::current_dir().ok()?;
cwd_resources_dir_from(&cwd)
}
pub fn global_memory_dir() -> Option<PathBuf> {
std::env::var("HOME")
.ok()
.map(|h| Path::new(&h).join(".zig").join("memory"))
}
pub fn global_memory_for(workflow: &str) -> Option<PathBuf> {
global_memory_dir().map(|d| d.join(workflow))
}
pub fn global_shared_memory_dir() -> Option<PathBuf> {
global_memory_dir().map(|d| d.join("_shared"))
}
pub fn ensure_global_memory_dir(child: Option<&str>) -> Result<PathBuf, ZigError> {
let mut dir = global_memory_dir()
.ok_or_else(|| ZigError::Io("HOME environment variable not set".into()))?;
if let Some(c) = child {
dir = dir.join(c);
}
if !dir.exists() {
std::fs::create_dir_all(&dir)
.map_err(|e| ZigError::Io(format!("failed to create {}: {e}", dir.display())))?;
}
Ok(dir)
}
pub fn cwd_memory_dir_from(start: &Path) -> Option<PathBuf> {
let mut current = start;
let stop = find_git_root(start);
loop {
let candidate = current.join(".zig").join("memory");
if candidate.is_dir() {
return Some(candidate);
}
if let Some(ref root) = stop {
if current == root.as_path() {
return None;
}
}
match current.parent() {
Some(p) => current = p,
None => return None,
}
}
}
pub fn cwd_memory_dir() -> Option<PathBuf> {
let cwd = std::env::current_dir().ok()?;
cwd_memory_dir_from(&cwd)
}
pub fn global_examples_dir() -> Option<PathBuf> {
global_base_dir().map(|d| d.join("examples"))
}
pub fn ensure_global_examples_dir() -> Result<PathBuf, ZigError> {
let dir = global_examples_dir()
.ok_or_else(|| ZigError::Io("HOME environment variable not set".into()))?;
if !dir.exists() {
std::fs::create_dir_all(&dir)
.map_err(|e| ZigError::Io(format!("failed to create {}: {e}", dir.display())))?;
}
Ok(dir)
}
pub fn global_base_dir() -> Option<PathBuf> {
std::env::var("HOME")
.ok()
.map(|h| Path::new(&h).join(".zig"))
}
pub fn sanitize_project_path(path: &str) -> String {
path.trim_start_matches('/').replace('/', "-")
}
fn find_git_root(start: &Path) -> Option<PathBuf> {
let mut current = start;
loop {
if current.join(".git").exists() {
return Some(current.to_path_buf());
}
current = current.parent()?;
}
}
pub fn project_dir(root: Option<&str>) -> Option<PathBuf> {
let base = global_base_dir()?;
if let Some(r) = root {
return Some(base.join("projects").join(sanitize_project_path(r)));
}
let cwd = std::env::current_dir().ok()?;
if let Some(git_root) = find_git_root(&cwd) {
let sanitized = sanitize_project_path(&git_root.to_string_lossy());
return Some(base.join("projects").join(sanitized));
}
Some(base)
}
pub fn project_logs_dir(root: Option<&str>) -> Option<PathBuf> {
project_dir(root).map(|p| p.join("logs"))
}
pub fn project_sessions_dir(root: Option<&str>) -> Option<PathBuf> {
project_logs_dir(root).map(|p| p.join("sessions"))
}
pub fn project_index_path(root: Option<&str>) -> Option<PathBuf> {
project_logs_dir(root).map(|p| p.join("index.json"))
}
pub fn global_sessions_index_path() -> Option<PathBuf> {
global_base_dir().map(|p| p.join("sessions_index.json"))
}
pub fn ensure_project_sessions_dir(root: Option<&str>) -> Result<PathBuf, ZigError> {
let dir = project_sessions_dir(root)
.ok_or_else(|| ZigError::Io("HOME environment variable not set".into()))?;
if !dir.exists() {
std::fs::create_dir_all(&dir)
.map_err(|e| ZigError::Io(format!("failed to create {}: {e}", dir.display())))?;
}
Ok(dir)
}
#[cfg(test)]
#[path = "paths_tests.rs"]
mod tests;