use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub struct ProjectContext {
pub index_id: String,
pub root_path: PathBuf,
pub detection_method: DetectionMethod,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DetectionMethod {
GitRoot,
MarkerFile,
Fallback,
}
pub fn detect_project(start: &Path) -> ProjectContext {
let mut current = start.to_path_buf();
loop {
if current.join(".git").exists() {
let name = basename(¤t);
return ProjectContext {
index_id: name,
root_path: current,
detection_method: DetectionMethod::GitRoot,
};
}
if current.join(".trusty-search").exists() {
let name = basename(¤t);
return ProjectContext {
index_id: name,
root_path: current,
detection_method: DetectionMethod::MarkerFile,
};
}
if !current.pop() {
break;
}
}
let cwd = start.to_path_buf();
let name = basename(&cwd);
ProjectContext {
index_id: name,
root_path: cwd,
detection_method: DetectionMethod::Fallback,
}
}
fn basename(p: &Path) -> String {
p.file_name()
.unwrap_or_default()
.to_string_lossy()
.into_owned()
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn test_detects_git_root() {
let tmp = tempdir_unique("detect-git");
fs::create_dir_all(tmp.join(".git")).unwrap();
let nested = tmp.join("a/b/c");
fs::create_dir_all(&nested).unwrap();
let ctx = detect_project(&nested);
assert_eq!(ctx.detection_method, DetectionMethod::GitRoot);
assert_eq!(ctx.root_path, tmp);
assert_eq!(ctx.index_id, basename(&tmp));
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_detects_marker_file() {
let tmp = tempdir_unique("detect-marker");
fs::create_dir_all(&tmp).unwrap();
fs::write(tmp.join(".trusty-search"), "index = \"x\"\n").unwrap();
let nested = tmp.join("sub");
fs::create_dir_all(&nested).unwrap();
let ctx = detect_project(&nested);
assert_eq!(ctx.detection_method, DetectionMethod::MarkerFile);
assert_eq!(ctx.root_path, tmp);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_falls_back_to_cwd_basename() {
let tmp = tempdir_unique("detect-fallback");
fs::create_dir_all(&tmp).unwrap();
let ctx = detect_project(&tmp);
assert_eq!(ctx.detection_method, DetectionMethod::Fallback);
assert_eq!(ctx.index_id, basename(&tmp));
let _ = fs::remove_dir_all(&tmp);
}
fn tempdir_unique(label: &str) -> PathBuf {
let pid = std::process::id();
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let p = std::env::temp_dir().join(format!("trusty-{}-{}-{}", label, pid, nanos));
let _ = std::fs::remove_dir_all(&p);
p
}
}