Replace InvalidRepoName(String) with RepoNameError enum; fix rebase issues
- Add RepoNameError enum with specific variants (Empty, MissingGitSuffix,
TooManySegments, PathOutsideBase, Invalid) instead of a plain String payload
- Error::Repo wraps RepoNameError via #[from]; validate_name now returns
Result<(), RepoNameError> so each failure site names the exact variant
- Fix AppResult->Result regression introduced by the rebase in repo_config
- Fix mirror.rs tracing macro: shorthand `mirror_url` (owned String) caused
str type inference to backflow into the let-Some binding; use %mirror_url
diff --git a/quire-server/src/error.rs b/quire-server/src/error.rs
index 87181a7..bc742cb 100644
--- a/quire-server/src/error.rs
+++ b/quire-server/src/error.rs
@@ -12,8 +12,9 @@ pub enum Error {
#[error("config not found: {0}")]
ConfigNotFound(String),
- #[error("{0}")]
- InvalidRepoName(String),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ Repo(#[from] RepoNameError),
#[error(transparent)]
#[diagnostic(transparent)]
@@ -38,6 +39,20 @@ pub enum Error {
pub type Result<T> = std::result::Result<T, Error>;
+#[derive(Debug, thiserror::Error, Diagnostic)]
+pub enum RepoNameError {
+ #[error("repository name cannot be empty")]
+ Empty,
+ #[error("repository name must end in .git: {0}")]
+ MissingGitSuffix(String),
+ #[error("repository name allows at most one level of grouping: {0}")]
+ TooManySegments(String),
+ #[error("path is not under repos directory: {0}")]
+ PathOutsideBase(String),
+ #[error("invalid repository name: {0}")]
+ Invalid(String),
+}
+
impl From<FennelError> for Error {
fn from(err: FennelError) -> Self {
Error::Fennel(Box::new(err))
diff --git a/quire-server/src/lib.rs b/quire-server/src/lib.rs
index 532b1ac..b3d9e86 100644
--- a/quire-server/src/lib.rs
+++ b/quire-server/src/lib.rs
@@ -8,5 +8,6 @@ pub mod quire;
pub use quire_core::telemetry::SentryConfig;
pub use error::Error;
+pub use error::RepoNameError;
pub use error::Result;
pub use quire::Quire;
diff --git a/quire-server/src/mirror.rs b/quire-server/src/mirror.rs
index c8cbf49..863292b 100644
--- a/quire-server/src/mirror.rs
+++ b/quire-server/src/mirror.rs
@@ -100,8 +100,8 @@ fn mirror_ref(
}
tracing::info!(
- ref_name = push_ref.ref_name,
- mirror_url,
+ ref_name = %push_ref.ref_name,
+ mirror_url = %mirror_url,
"mirror: push succeeded"
);
Ok(())
diff --git a/quire-server/src/quire/mod.rs b/quire-server/src/quire/mod.rs
index 04f688e..9d15310 100644
--- a/quire-server/src/quire/mod.rs
+++ b/quire-server/src/quire/mod.rs
@@ -5,7 +5,7 @@ use std::sync::{Arc, Mutex, OnceLock};
pub mod web;
use crate::ci::{Ci, Executor, Runs};
-use crate::{Error, Result};
+use crate::{Error, RepoNameError, Result};
pub use quire_core::telemetry::SentryConfig;
use quire_core::fennel::Fennel;
@@ -86,10 +86,7 @@ impl Repo {
/// Used by hooks that receive `GIT_DIR` from git.
pub fn from_path(repos_base: &Path, path: &Path) -> Result<Self> {
let Ok(relative) = path.strip_prefix(repos_base) else {
- return Err(Error::InvalidRepoName(format!(
- "path is not under repos directory: {}",
- path.display()
- )));
+ return Err(RepoNameError::PathOutsideBase(path.display().to_string()).into());
};
let name = relative.to_string_lossy();
Self::validate_name(&name)?;
@@ -99,40 +96,28 @@ impl Repo {
})
}
- fn validate_name(name: &str) -> Result<()> {
+ fn validate_name(name: &str) -> std::result::Result<(), RepoNameError> {
if name.is_empty() {
- return Err(Error::InvalidRepoName(
- "repository name cannot be empty".to_string(),
- ));
+ return Err(RepoNameError::Empty);
}
if name.contains("..") {
- return Err(Error::InvalidRepoName(format!(
- "invalid repository name: {name}"
- )));
+ return Err(RepoNameError::Invalid(name.to_string()));
}
if !name.ends_with(".git") {
- return Err(Error::InvalidRepoName(format!(
- "repository name must end in .git: {name}"
- )));
+ return Err(RepoNameError::MissingGitSuffix(name.to_string()));
}
if name.contains("//") {
- return Err(Error::InvalidRepoName(format!(
- "invalid repository name: {name}"
- )));
+ return Err(RepoNameError::Invalid(name.to_string()));
}
let segments = name.split('/').collect::<Vec<_>>();
if segments.len() > 2 {
- return Err(Error::InvalidRepoName(format!(
- "repository name allows at most one level of grouping: {name}"
- )));
+ return Err(RepoNameError::TooManySegments(name.to_string()));
}
for seg in &segments {
if seg.is_empty() || *seg == "." || *seg == ".." || *seg == ".git" {
- return Err(Error::InvalidRepoName(format!(
- "invalid repository name: {name}"
- )));
+ return Err(RepoNameError::Invalid(name.to_string()));
}
}
@@ -170,7 +155,7 @@ impl Repo {
/// Read and parse `.quire/config.fnl` at the given commit SHA.
///
/// Returns defaults if the file does not exist at that commit.
- pub fn repo_config(&self, sha: &str) -> AppResult<RepoConfig> {
+ pub fn repo_config(&self, sha: &str) -> Result<RepoConfig> {
let path = format!("{sha}:.quire/config.fnl");
// cat-file -e: exit 0 if the object exists, non-zero if absent.