From: Alpha Chen Date: Sat, 25 Apr 2026 14:38:19 +0000 (+0000) Subject: Extract Repo struct wrapping a resolved path X-Git-Url: http://quire.kejadlen.dev/?a=commitdiff_plain;h=67de6284c00b7b84829d04c9a9a645c57eac9f20;p=quire.git Extract Repo struct wrapping a resolved path Gives commands a typed handle instead of a bare PathBuf, with room to add methods later. Assisted-by: GLM-5.1 via pi --- diff --git a/src/bin/quire/commands/exec.rs b/src/bin/quire/commands/exec.rs index f66d2f3..f408924 100644 --- a/src/bin/quire/commands/exec.rs +++ b/src/bin/quire/commands/exec.rs @@ -44,11 +44,14 @@ fn dispatch_git(quire: &Quire, git_cmd: &str, args: &[String]) -> Result<()> { let path = args[0].trim_start_matches('/'); ensure!(!path.is_empty(), "empty repository path"); - let repo_dir = quire.repo(path)?; - ensure!(repo_dir.is_dir(), "repository not found: {path}"); + let repo = quire.repo(path)?; + ensure!(repo.exists(), "repository not found: {path}"); tracing::info!(%git_cmd, %path, "dispatching git command"); - let err = Command::new(git_cmd).arg(".").current_dir(&repo_dir).exec(); + let err = Command::new(git_cmd) + .arg(".") + .current_dir(repo.path()) + .exec(); bail!("exec failed: {err}") } diff --git a/src/bin/quire/commands/repo.rs b/src/bin/quire/commands/repo.rs index bff0b27..557c59c 100644 --- a/src/bin/quire/commands/repo.rs +++ b/src/bin/quire/commands/repo.rs @@ -5,11 +5,11 @@ use miette::{IntoDiagnostic, Result, ensure}; use quire::Quire; pub async fn new(quire: &Quire, name: &str) -> Result<()> { - let repo_dir = quire.repo(name)?; - ensure!(!repo_dir.is_dir(), "repository already exists: {name}"); + let repo = quire.repo(name)?; + ensure!(!repo.exists(), "repository already exists: {name}"); // Create parent directory for grouped repos (e.g. work/foo.git). - if let Some(parent) = repo_dir.parent() { + if let Some(parent) = repo.path().parent() { fs_err::create_dir_all(parent).into_diagnostic()?; } @@ -35,13 +35,13 @@ pub async fn list(quire: &Quire) -> Result<()> { } pub async fn rm(quire: &Quire, name: &str) -> Result<()> { - let repo_dir = quire.repo(name)?; - ensure!(repo_dir.is_dir(), "repository not found: {name}"); + let repo = quire.repo(name)?; + ensure!(repo.exists(), "repository not found: {name}"); - fs_err::remove_dir_all(&repo_dir).into_diagnostic()?; + fs_err::remove_dir_all(repo.path()).into_diagnostic()?; // Clean up empty parent directory for grouped repos. - if let Some(parent) = repo_dir.parent() + if let Some(parent) = repo.path().parent() && parent != quire.repos_dir() { let _ = fs_err::remove_dir(parent); diff --git a/src/quire.rs b/src/quire.rs index 66fef54..6227fb2 100644 --- a/src/quire.rs +++ b/src/quire.rs @@ -4,6 +4,23 @@ use miette::{IntoDiagnostic, Result, ensure}; use crate::config::Config; +/// A resolved repository path. +/// +/// Created by `Quire::repo` after validating the name. +pub struct Repo { + path: PathBuf, +} + +impl Repo { + pub fn path(&self) -> &Path { + &self.path + } + + pub fn exists(&self) -> bool { + self.path.is_dir() + } +} + /// Application runtime context. /// /// Carries configuration and provides resolved paths to repositories. @@ -25,9 +42,11 @@ impl Quire { /// /// Rejects path traversal, missing `.git` suffix, empty segments, /// reserved path components, and more than one level of grouping. - pub fn repo(&self, name: &str) -> Result { + pub fn repo(&self, name: &str) -> Result { validate_repo_name(name)?; - Ok(self.config.repos_dir.join(name)) + Ok(Repo { + path: self.config.repos_dir.join(name), + }) } /// List all repository names under the repos directory. @@ -124,8 +143,8 @@ mod tests { fn repo_resolves_path() { let q = quire(); assert_eq!( - q.repo("foo.git").unwrap(), - PathBuf::from("/var/quire/repos/foo.git") + q.repo("foo.git").unwrap().path(), + Path::new("/var/quire/repos/foo.git") ); }