🥇 export from upstream (be15b0d)
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
use miette::Diagnostic;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
pub enum WorkspaceError {
|
||||
#[error("Failed to resolve absolute path for {0}: {1}")]
|
||||
#[diagnostic(code(sandcage::workspace::canonicalize_failed))]
|
||||
CanonicalizeFailed(PathBuf, #[source] std::io::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, WorkspaceError>;
|
||||
|
||||
pub fn resolve_workspace(path: Option<&Path>) -> Result<PathBuf> {
|
||||
// Determine starting path
|
||||
let raw = match path {
|
||||
Some(p) => p.to_path_buf(),
|
||||
None => std::env::current_dir()
|
||||
.map_err(|e| WorkspaceError::CanonicalizeFailed(PathBuf::from("."), e))?,
|
||||
};
|
||||
|
||||
// Resolve to absolute, canonical path (follows symlinks, checks existence)
|
||||
let absolute = raw
|
||||
.canonicalize()
|
||||
.map_err(|e| WorkspaceError::CanonicalizeFailed(raw.clone(), e))?;
|
||||
|
||||
// Ask git for the worktree root; silently fall back if not a git repo
|
||||
let git_root = Command::new("git")
|
||||
.args(["rev-parse", "--show-toplevel"])
|
||||
.current_dir(&absolute)
|
||||
.output();
|
||||
|
||||
match git_root {
|
||||
Ok(output) if output.status.success() => {
|
||||
let root = String::from_utf8_lossy(&output.stdout)
|
||||
.trim()
|
||||
.to_string();
|
||||
Ok(PathBuf::from(root))
|
||||
}
|
||||
// Not a git repo, or git not installed — return the absolute path as-is
|
||||
_ => Ok(absolute),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::process::Command;
|
||||
use tempfile::TempDir;
|
||||
|
||||
/// Create a temporary directory and initialise it as a git repository.
|
||||
/// Returns the `TempDir` guard (keep it alive for the test) and the path.
|
||||
fn make_git_repo() -> (TempDir, PathBuf) {
|
||||
let dir = TempDir::new().expect("create tempdir");
|
||||
let path = dir.path().to_path_buf();
|
||||
|
||||
// git init
|
||||
let status = Command::new("git")
|
||||
.args(["init"])
|
||||
.current_dir(&path)
|
||||
.status()
|
||||
.expect("run git init");
|
||||
assert!(status.success(), "git init failed");
|
||||
|
||||
(dir, path)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_repo_root_when_inside_git_repo() {
|
||||
let (_guard, repo_root) = make_git_repo();
|
||||
|
||||
// Create a nested directory inside the repo
|
||||
let nested = repo_root.join("a").join("b");
|
||||
std::fs::create_dir_all(&nested).expect("create nested dirs");
|
||||
|
||||
// resolve_workspace from the nested dir should return the repo root
|
||||
let resolved = resolve_workspace(Some(&nested)).expect("resolve_workspace");
|
||||
assert_eq!(
|
||||
resolved.canonicalize().unwrap(),
|
||||
repo_root.canonicalize().unwrap(),
|
||||
"expected repo root, got {resolved:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_directory_itself_when_not_in_git_repo() {
|
||||
// Create a temp dir that is NOT a git repo (no git init)
|
||||
let dir = TempDir::new().expect("create tempdir");
|
||||
|
||||
// Make sure there is no ancestor git repo that would pollute the result.
|
||||
// tempfile creates dirs under the system temp folder, which is typically
|
||||
// outside any user repo, so this should be safe.
|
||||
let path = dir.path().to_path_buf();
|
||||
let resolved = resolve_workspace(Some(&path)).expect("resolve_workspace");
|
||||
|
||||
assert_eq!(
|
||||
resolved.canonicalize().unwrap(),
|
||||
path.canonicalize().unwrap(),
|
||||
"expected the directory itself, got {resolved:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn none_path_uses_current_directory() {
|
||||
// When None is passed the function should not error; it should return
|
||||
// something (either the cwd or its git root).
|
||||
let result = resolve_workspace(None);
|
||||
assert!(result.is_ok(), "resolve_workspace(None) returned error: {result:?}");
|
||||
let resolved = result.unwrap();
|
||||
assert!(resolved.is_absolute(), "result should be absolute: {resolved:?}");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user