use std::path::{Path, PathBuf};
pub fn find_project_root_from(start_dir: &Path) -> Option<PathBuf> {
let mut current = start_dir.to_path_buf();
loop {
let candidate = current.join(".life");
if candidate.is_dir() {
return Some(current);
}
if !current.pop() {
return None;
}
}
}
pub fn find_project_root() -> Option<PathBuf> {
let cwd = std::env::current_dir().ok()?;
find_project_root_from(&cwd)
}
pub fn life_dir() -> PathBuf {
if let Some(root) = find_project_root() {
root.join(".life")
} else {
global_life_dir()
}
}
pub fn global_life_dir() -> PathBuf {
dirs::home_dir()
.expect("home directory must be resolvable")
.join(".life")
}
pub fn resolve_module_dir(module: &str, cli_override: Option<&Path>) -> PathBuf {
if let Some(p) = cli_override {
return p.to_path_buf();
}
if let Some(root) = find_project_root() {
let local = root.join(".life").join(module);
if local.exists() {
return local;
}
}
global_life_dir().join(module)
}
pub fn is_initialized() -> bool {
if find_project_root().is_some() {
return true;
}
global_life_dir().exists()
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn find_in_current_dir() {
let tmp = TempDir::new().unwrap();
std::fs::create_dir(tmp.path().join(".life")).unwrap();
let result = find_project_root_from(tmp.path());
assert_eq!(result, Some(tmp.path().to_path_buf()));
}
#[test]
fn find_in_parent_dir() {
let tmp = TempDir::new().unwrap();
std::fs::create_dir(tmp.path().join(".life")).unwrap();
let child = tmp.path().join("sub").join("deep");
std::fs::create_dir_all(&child).unwrap();
let result = find_project_root_from(&child);
assert_eq!(result, Some(tmp.path().to_path_buf()));
}
#[test]
fn not_found() {
let tmp = TempDir::new().unwrap();
let result = find_project_root_from(tmp.path());
assert!(result.is_none());
}
#[test]
fn module_with_override() {
let tmp = TempDir::new().unwrap();
let override_dir = tmp.path().join("custom");
std::fs::create_dir(&override_dir).unwrap();
let result = resolve_module_dir("lago", Some(&override_dir));
assert_eq!(result, override_dir);
}
#[test]
fn module_without_override_falls_to_global() {
let result = resolve_module_dir("lago", None);
assert!(result.ends_with(".life/lago") || result.ends_with(".life\\lago"));
}
#[test]
fn global_under_home() {
let home = dirs::home_dir().unwrap();
assert_eq!(global_life_dir(), home.join(".life"));
}
}