use std::path::{Path, PathBuf};
pub fn repo_root(start: &Path) -> Option<PathBuf> {
let mut cur = Some(start);
while let Some(dir) = cur {
if dir.join(".git").exists() {
return Some(dir.to_path_buf());
}
cur = dir.parent();
}
None
}
fn baseplate_home_resolver() -> Option<PathBuf> {
let home = std::env::var("BASEPLATE_HOME").ok()?;
let candidate = PathBuf::from(home);
if candidate.is_dir() {
Some(candidate)
} else {
None
}
}
pub fn framework_root() -> PathBuf {
if let Ok(exe) = std::env::current_exe() {
if let Some(root) = repo_root(&exe) {
return root;
}
}
if let Some(root) = baseplate_home_resolver() {
return root;
}
if let Ok(cwd) = std::env::current_dir() {
if let Some(root) = repo_root(&cwd) {
return root;
}
return cwd;
}
PathBuf::from(".")
}
#[cfg(test)]
mod tests {
use super::*;
static PATHS_ENV_GUARD: std::sync::Mutex<()> = std::sync::Mutex::new(());
struct TempDir(PathBuf);
impl Drop for TempDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.0);
}
}
fn unique_tmp(label: &str) -> TempDir {
let unique = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
let dir = std::env::temp_dir().join(format!(
"baseplate-paths-test-{label}-{}-{unique}",
std::process::id()
));
std::fs::create_dir_all(&dir).expect("create temp dir");
TempDir(dir)
}
#[test]
fn repo_root_walks_up_to_git() {
let here = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let Some(root) = repo_root(here) else {
eprintln!("skip: no .git ancestor — unpacked/temp build");
return;
};
assert!(root.join(".git").exists());
}
#[test]
fn baseplate_home_resolves_when_dir_exists() {
let _guard = PATHS_ENV_GUARD.lock().unwrap();
let tmp = unique_tmp("home");
let original = std::env::var("BASEPLATE_HOME").ok();
std::env::set_var("BASEPLATE_HOME", &tmp.0);
let resolved = baseplate_home_resolver();
match original {
Some(v) => std::env::set_var("BASEPLATE_HOME", v),
None => std::env::remove_var("BASEPLATE_HOME"),
}
assert_eq!(
resolved,
Some(tmp.0.clone()),
"BASEPLATE_HOME must resolve when it names an existing directory"
);
}
#[test]
fn baseplate_home_none_when_absent() {
let _guard = PATHS_ENV_GUARD.lock().unwrap();
let original = std::env::var("BASEPLATE_HOME").ok();
std::env::set_var("BASEPLATE_HOME", "/nonexistent-baseplate-home-dir-xyz");
let resolved = baseplate_home_resolver();
match original {
Some(v) => std::env::set_var("BASEPLATE_HOME", v),
None => std::env::remove_var("BASEPLATE_HOME"),
}
assert_eq!(
resolved, None,
"a BASEPLATE_HOME that is not an existing directory must not resolve"
);
}
}