radicle/git/raw: Introduce `trait ErrorExt`

This extension trait is more ergonomic than what `radicle-git-ext`
provides.
This commit is contained in:
Lorenz Leutgeb 2025-10-04 11:59:26 +02:00
parent 837f4694df
commit ea562215ea
No known key found for this signature in database
11 changed files with 58 additions and 23 deletions

View File

@ -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()),

View File

@ -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,

View File

@ -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<R: ReadRepository>(
) -> Result<Option<git::Oid>, 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),
}
}

View File

@ -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<Oid, error::Ancestry> {
.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 }),
}
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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,
}
}
}

View File

@ -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());

View File

@ -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,
}

View File

@ -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()),
}
}

View File

@ -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)