From ea562215eaa163568c3f1061e4cbbd907e9913db Mon Sep 17 00:00:00 2001 From: Lorenz Leutgeb Date: Sat, 4 Oct 2025 11:59:26 +0200 Subject: [PATCH] radicle/git/raw: Introduce `trait ErrorExt` This extension trait is more ergonomic than what `radicle-git-ext` provides. --- crates/radicle-cli/src/commands/init.rs | 3 ++- .../src/commands/patch/checkout.rs | 5 ++-- crates/radicle-cli/src/commands/watch.rs | 3 ++- crates/radicle-fetch/src/git/repository.rs | 3 ++- crates/radicle/src/git.rs | 7 +++--- crates/radicle/src/git/canonical/effects.rs | 5 ++-- crates/radicle/src/git/raw.rs | 24 +++++++++++++++++++ crates/radicle/src/identity/doc.rs | 11 +++++---- crates/radicle/src/storage.rs | 9 +++---- crates/radicle/src/storage/git.rs | 3 ++- crates/radicle/src/storage/refs.rs | 8 +++---- 11 files changed, 58 insertions(+), 23 deletions(-) diff --git a/crates/radicle-cli/src/commands/init.rs b/crates/radicle-cli/src/commands/init.rs index a68ea8da..f79d004b 100644 --- a/crates/radicle-cli/src/commands/init.rs +++ b/crates/radicle-cli/src/commands/init.rs @@ -13,6 +13,7 @@ use serde_json as json; use radicle::crypto::ssh; use radicle::explorer::ExplorerUrl; use radicle::git::raw; +use radicle::git::raw::ErrorExt as _; use radicle::git::RefString; use radicle::identity::project::ProjectName; use radicle::identity::{Doc, RepoId, Visibility}; @@ -189,7 +190,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { let path = options.path.as_deref().unwrap_or(cwd.as_path()); let repo = match git::Repository::open(path) { Ok(r) => r, - Err(e) if radicle::git::ext::is_not_found_err(&e) => { + Err(e) if e.is_not_found() => { anyhow::bail!("a Git repository was not found at the given path") } Err(e) => return Err(e.into()), diff --git a/crates/radicle-cli/src/commands/patch/checkout.rs b/crates/radicle-cli/src/commands/patch/checkout.rs index 5ea1a630..9fdec248 100644 --- a/crates/radicle-cli/src/commands/patch/checkout.rs +++ b/crates/radicle-cli/src/commands/patch/checkout.rs @@ -3,6 +3,7 @@ use anyhow::anyhow; use git_ref_format::Qualified; use radicle::cob::patch; use radicle::cob::patch::RevisionId; +use radicle::git::raw::ErrorExt as _; use radicle::git::RefString; use radicle::patch::cache::Patches as _; use radicle::patch::PatchId; @@ -73,7 +74,7 @@ pub fn run( } head } - Err(e) if radicle::git::is_not_found_err(&e) => { + Err(e) if e.is_not_found() => { let commit = find_patch_commit(revision, stored, working)?; // Create patch branch and switch to it. working.branch(patch_branch.as_str(), &commit, true)?; @@ -128,7 +129,7 @@ fn find_patch_commit<'a>( match working.find_commit(head) { Ok(commit) => Ok(commit), - Err(e) if git::ext::is_not_found_err(&e) => { + Err(e) if e.is_not_found() => { let output = git::process::fetch_pack( Some(working.path()), stored, diff --git a/crates/radicle-cli/src/commands/watch.rs b/crates/radicle-cli/src/commands/watch.rs index 6f2b13ca..c854ccb9 100644 --- a/crates/radicle-cli/src/commands/watch.rs +++ b/crates/radicle-cli/src/commands/watch.rs @@ -4,6 +4,7 @@ use std::{thread, time}; use anyhow::{anyhow, Context as _}; use radicle::git; +use radicle::git::raw::ErrorExt as _; use radicle::prelude::{NodeId, RepoId}; use radicle::storage::{ReadRepository, ReadStorage}; @@ -169,7 +170,7 @@ fn reference( ) -> Result, git::raw::Error> { match repo.reference_oid(nid, qual) { Ok(oid) => Ok(Some(oid)), - Err(e) if git::ext::is_not_found_err(&e) => Ok(None), + Err(e) if e.is_not_found() => Ok(None), Err(e) => Err(e), } } diff --git a/crates/radicle-fetch/src/git/repository.rs b/crates/radicle-fetch/src/git/repository.rs index 8c32f5e0..e4e8ca8f 100644 --- a/crates/radicle-fetch/src/git/repository.rs +++ b/crates/radicle-fetch/src/git/repository.rs @@ -1,6 +1,7 @@ pub mod error; use either::Either; +use radicle::git::raw::ErrorExt as _; use radicle::git::{self, Namespaced, Oid, Qualified}; use radicle::storage::git::Repository; @@ -53,7 +54,7 @@ fn find_and_peel(repo: &Repository, oid: Oid) -> Result { .map_err(|err| error::Ancestry::Peel { oid, err })? .id() .into()), - Err(e) if git::is_not_found_err(&e) => Err(error::Ancestry::Missing { oid }), + Err(e) if e.is_not_found() => Err(error::Ancestry::Missing { oid }), Err(err) => Err(error::Ancestry::Object { oid, err }), } } diff --git a/crates/radicle/src/git.rs b/crates/radicle/src/git.rs index 9b70c81d..676e990f 100644 --- a/crates/radicle/src/git.rs +++ b/crates/radicle/src/git.rs @@ -17,7 +17,6 @@ use crate::storage; use crate::storage::refs::Refs; use crate::storage::RemoteId; -pub use ext::is_not_found_err; pub use ext::Error; pub use ext::NotFound; pub use ext::Oid; @@ -31,6 +30,8 @@ pub use radicle_git_ext as ext; pub use storage::git::transport::local::Url; pub use storage::BranchName; +use raw::ErrorExt as _; + /// Default port of the `git` transport protocol. pub const PROTOCOL_PORT: u16 = 9418; /// Minimum required git version. @@ -733,14 +734,14 @@ pub fn set_upstream( let branch_merge = format!("branch.{branch}.merge"); config.remove_multivar(&branch_remote, ".*").or_else(|e| { - if ext::is_not_found_err(&e) { + if e.is_not_found() { Ok(()) } else { Err(e) } })?; config.remove_multivar(&branch_merge, ".*").or_else(|e| { - if ext::is_not_found_err(&e) { + if e.is_not_found() { Ok(()) } else { Err(e) diff --git a/crates/radicle/src/git/canonical/effects.rs b/crates/radicle/src/git/canonical/effects.rs index 65c95bea..54ca20d5 100644 --- a/crates/radicle/src/git/canonical/effects.rs +++ b/crates/radicle/src/git/canonical/effects.rs @@ -1,6 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use crate::git; +use crate::git::raw::ErrorExt as _; use crate::git::{Oid, Qualified}; use crate::prelude::Did; @@ -215,7 +216,7 @@ impl FindObjects for git::raw::Repository { let name = &refname.with_namespace(did.as_key().into()); let reference = match self.find_reference(name.as_str()) { Ok(reference) => reference, - Err(e) if git::ext::is_not_found_err(&e) => { + Err(e) if e.is_not_found() => { missing_refs.insert(name.to_owned()); continue; } @@ -235,7 +236,7 @@ impl FindObjects for git::raw::Repository { object.kind().map(|kind| kind.to_string()), ) }), - Err(err) if git::ext::is_not_found_err(&err) => { + Err(err) if err.is_not_found() => { missing_objects.insert(*did, oid); continue; } diff --git a/crates/radicle/src/git/raw.rs b/crates/radicle/src/git/raw.rs index 3a9c0522..fdeb96b1 100644 --- a/crates/radicle/src/git/raw.rs +++ b/crates/radicle/src/git/raw.rs @@ -32,3 +32,27 @@ pub(crate) mod transport { register, Service, SmartSubtransport, SmartSubtransportStream, Transport, }; } + +/// An extension trait for [`git2::Error`] to more conveniently handle +/// errors with the code [`git2::ErrorCode::NotFound`]. +pub trait ErrorExt { + /// Returns `true` if the error associated with this error is [`git2::ErrorCode::NotFound`]. + fn is_not_found(&self) -> bool; +} + +impl ErrorExt for git2::Error { + fn is_not_found(&self) -> bool { + self.code() == git2::ErrorCode::NotFound + } +} + +impl ErrorExt for git_ext::Error { + fn is_not_found(&self) -> bool { + use git_ext::Error::*; + match self { + Git(e) => e.is_not_found(), + NotFound(_) => true, + _ => false, + } + } +} diff --git a/crates/radicle/src/identity/doc.rs b/crates/radicle/src/identity/doc.rs index 305766c2..6617265e 100644 --- a/crates/radicle/src/identity/doc.rs +++ b/crates/radicle/src/identity/doc.rs @@ -22,6 +22,7 @@ use crate::crypto; use crate::crypto::Signature; use crate::git; use crate::git::canonical::rules; +use crate::git::raw::ErrorExt as _; use crate::identity::{project::Project, Did}; use crate::node::device::Device; use crate::storage; @@ -71,9 +72,8 @@ impl DocError { /// Whether this error is caused by the document not being found. pub fn is_not_found(&self) -> bool { match self { - Self::GitExt(git::Error::NotFound(_)) => true, - Self::GitExt(git::Error::Git(e)) if git::is_not_found_err(e) => true, - Self::Git(err) if git::is_not_found_err(err) => true, + Self::GitExt(e) => e.is_not_found(), + Self::Git(e) => e.is_not_found(), _ => false, } } @@ -1160,7 +1160,10 @@ mod test { let oid = git::raw::Oid::from_str("2d52a53ce5e4f141148a5f770cfd3ead2d6a45b8").unwrap(); let err = repo.identity_head_of(&remote).unwrap_err(); - matches!(err, git::ext::Error::NotFound(_)); + { + use crate::git::raw::ErrorExt as _; + assert!(err.is_not_found()); + } let err = Doc::load_at(oid.into(), &repo).unwrap_err(); assert!(err.is_not_found()); diff --git a/crates/radicle/src/storage.rs b/crates/radicle/src/storage.rs index 7fbe5e13..3e9a830f 100644 --- a/crates/radicle/src/storage.rs +++ b/crates/radicle/src/storage.rs @@ -16,6 +16,7 @@ pub use radicle_git_ext::Oid; use crate::cob; use crate::collections::RandomMap; +use crate::git::raw::ErrorExt as _; use crate::git::{canonical, ext as git_ext}; use crate::git::{refspec::Refspec, PatternString, Qualified, RefError, RefStr, RefString}; use crate::identity::{doc, Did, PayloadError}; @@ -133,9 +134,9 @@ pub enum RepositoryError { impl RepositoryError { pub fn is_not_found(&self) -> bool { match self { - Self::Storage(e) if e.is_not_found() => true, - Self::Git(e) if git_ext::is_not_found_err(e) => true, - Self::GitExt(git_ext::Error::NotFound(_)) => true, + Self::Storage(e) => e.is_not_found(), + Self::GitExt(e) => e.is_not_found(), + Self::Git(e) => e.is_not_found(), _ => false, } } @@ -167,7 +168,7 @@ impl Error { pub fn is_not_found(&self) -> bool { match self { Self::Io(e) if e.kind() == io::ErrorKind::NotFound => true, - Self::Git(e) if git::ext::is_not_found_err(e) => true, + Self::Git(e) if e.is_not_found() => true, Self::Doc(e) if e.is_not_found() => true, _ => false, } diff --git a/crates/radicle/src/storage/git.rs b/crates/radicle/src/storage/git.rs index 2e11130a..bdfea41e 100644 --- a/crates/radicle/src/storage/git.rs +++ b/crates/radicle/src/storage/git.rs @@ -14,6 +14,7 @@ use std::{fs, io}; use crypto::Verified; use crate::git::canonical::Quorum; +use crate::git::raw::ErrorExt as _; use crate::identity::crefs::GetCanonicalRefs as _; use crate::identity::doc::DocError; use crate::identity::{CanonicalRefs, Doc, DocAt, RepoId}; @@ -817,7 +818,7 @@ impl ReadRepository for Repository { match result { Ok(oid) => Ok(oid), - Err(err) if git::ext::is_not_found_err(&err) => self.canonical_identity_head(), + Err(err) if err.is_not_found() => self.canonical_identity_head(), Err(err) => Err(err.into()), } } diff --git a/crates/radicle/src/storage/refs.rs b/crates/radicle/src/storage/refs.rs index 74ac32c6..9a109d71 100644 --- a/crates/radicle/src/storage/refs.rs +++ b/crates/radicle/src/storage/refs.rs @@ -14,6 +14,7 @@ use thiserror::Error; use crate::git; use crate::git::ext as git_ext; +use crate::git::raw::ErrorExt as _; use crate::git::Oid; use crate::node::device::Device; use crate::profile::env; @@ -64,9 +65,8 @@ impl Error { /// Whether this error is caused by a reference not being found. pub fn is_not_found(&self) -> bool { match self { - Self::GitExt(git::Error::NotFound(_)) => true, - Self::GitExt(git::Error::Git(e)) if git::is_not_found_err(e) => true, - Self::Git(e) if git::is_not_found_err(e) => true, + Self::GitExt(e) => e.is_not_found(), + Self::Git(e) => e.is_not_found(), _ => false, } } @@ -430,7 +430,7 @@ impl SignedRefsAt { { let at = match RefsAt::new(repo, remote) { Ok(RefsAt { at, .. }) => at, - Err(e) if git::is_not_found_err(&e) => return Ok(None), + Err(e) if e.is_not_found() => return Ok(None), Err(e) => return Err(e.into()), }; Self::load_at(at, remote, repo).map(Some)