Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,10 @@ jobs:
# due to being more in line with some Unix-oriented assumptions. So we need to verify that
# the test suite is compatible with being run even outside that environment, including that
# `gix-testtools` is still able to run fixture scripts with the `bash` shell as intended.
# `gix-error` is excluded because it gets compiled with the wrong feature toggles here.
# It's tested individually.
run: | # zizmor: ignore[template-injection]
cargo nextest run --workspace --no-fail-fast -- ${{ matrix.test-args }}
cargo nextest run --workspace --no-fail-fast --exclude gix-error -- ${{ matrix.test-args }}
- name: Check that tracked archives are up to date
run: |
# If this fails, the fix is usually to commit a regenerated archive.
Expand Down Expand Up @@ -341,7 +343,7 @@ jobs:
GIX_TEST_IGNORE_ARCHIVES: '1'
TEST_ARGS: ${{ matrix.test-args }}
run: |
cargo nextest --profile=with-xml run --workspace --no-fail-fast -- `
cargo nextest --profile=with-xml run --workspace --no-fail-fast --exclude gix-error -- `
(-split $Env:TEST_ARGS)
continue-on-error: true
- name: Check for errors
Expand Down Expand Up @@ -427,7 +429,7 @@ jobs:
- name: Test (nextest)
env:
GIX_TEST_IGNORE_ARCHIVES: '1'
run: cargo nextest run --workspace --no-fail-fast
run: cargo nextest run --workspace --no-fail-fast --exclude gix-error

test-32bit-windows-size-doc:
runs-on: windows-latest
Expand Down
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions gitoxide-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ gix-transport-configuration-only = { package = "gix-transport", version = "^0.52
gix-archive-for-configuration-only = { package = "gix-archive", version = "^0.26.0", path = "../gix-archive", optional = true, features = ["tar", "tar_gz"] }
gix-status = { version = "^0.24.0", path = "../gix-status" }
gix-fsck = { version = "^0.16.0", path = "../gix-fsck" }
gix-error-for-configuration-only = { package = "gix-error", version = "^0.0.0", path = "../gix-error", features = ["anyhow"] }
serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] }
anyhow = "1.0.100"
thiserror = "2.0.17"
Expand Down
5 changes: 2 additions & 3 deletions gitoxide-core/src/commitgraph/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ pub(crate) mod function {
use crate::OutputFormat;
use anyhow::Result;
use gix::commitgraph::{verify::Outcome, Graph};
use gix::Exn;

pub fn verify<W1, W2>(
path: impl AsRef<Path>,
Expand All @@ -39,13 +38,13 @@ pub(crate) mod function {
W1: io::Write,
W2: io::Write,
{
let g = Graph::at(path.as_ref()).map_err(Exn::into_error)?;
let g = Graph::at(path.as_ref())?;

#[allow(clippy::unnecessary_wraps, unknown_lints)]
fn noop_processor(_commit: &gix::commitgraph::file::Commit<'_>) -> std::result::Result<(), std::fmt::Error> {
Ok(())
}
let stats = g.verify_integrity(noop_processor).map_err(Exn::into_error)?;
let stats = g.verify_integrity(noop_processor)?;

#[cfg_attr(not(feature = "serde"), allow(clippy::single_match))]
match output_statistics {
Expand Down
3 changes: 1 addition & 2 deletions gitoxide-core/src/repository/commitgraph/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ pub(crate) mod function {

use crate::{repository::commitgraph::verify::Context, OutputFormat};
use anyhow::Result;
use gix::Exn;

pub fn verify<W1, W2>(
repo: gix::Repository,
Expand All @@ -34,7 +33,7 @@ pub(crate) mod function {
fn noop_processor(_commit: &gix::commitgraph::file::Commit<'_>) -> std::result::Result<(), std::fmt::Error> {
Ok(())
}
let stats = g.verify_integrity(noop_processor).map_err(Exn::into_error)?;
let stats = g.verify_integrity(noop_processor)?;

#[cfg_attr(not(feature = "serde"), allow(clippy::single_match))]
match output_statistics {
Expand Down
4 changes: 2 additions & 2 deletions gitoxide-core/src/repository/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ fn resolve_revspec(
// When the revspec is just a name, the delegate tries to resolve a reference which fails.
// We extract the error from the tree to learn the name, and treat it as file.
let not_found = err
.iter_frames()
.find_map(|f| f.error().downcast_ref::<gix::refs::file::find::existing::Error>());
.sources()
.find_map(|err| err.downcast_ref::<gix::refs::file::find::existing::Error>());
if let Some(gix::refs::file::find::existing::Error::NotFound { name }) = not_found {
let root = repo.workdir().map(ToOwned::to_owned);
let name = gix::path::os_string_into_bstring(name.into())?;
Expand Down
2 changes: 1 addition & 1 deletion gix-commitgraph/src/file/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ impl Iterator for Parents<'_> {
self.next()
} else {
Some(Err(message!(
"commit {0}'s extra edges overflows the commit-graph file's extra edges list",
"commit {}'s extra edges overflows the commit-graph file's extra edges list",
self.commit_data.id()
)))
}
Expand Down
7 changes: 1 addition & 6 deletions gix-commitgraph/src/file/init.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::path::{Path, PathBuf};

use bstr::ByteSlice;
use gix_error::{message, ErrorExt, Exn, Message, ResultExt};

use crate::{
Expand Down Expand Up @@ -142,11 +141,7 @@ impl File {
}

if base_graph_count > 0 && base_graphs_list_offset.is_none() {
return Err(message!(
"Chunk named {:?} was not found in chunk file index",
BASE_GRAPHS_LIST_CHUNK_ID.as_bstr()
)
.into());
return Err(message!("Chunk named {BASE_GRAPHS_LIST_CHUNK_ID:?} was not found in chunk file index").into());
}

let (fan, _) = read_fan(&data[fan_offset..]);
Expand Down
9 changes: 4 additions & 5 deletions gix-commitgraph/src/file/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,9 @@ impl File {
.raise());
}
return Err(message!(
"commit at file position {} with ID {} is out of order relative to its predecessor with ID {}",
"commit at file position {} with ID {} is out of order relative to its predecessor with ID {prev_id}",
commit.position(),
commit.id(),
prev_id
commit.id()
)
.raise());
}
Expand Down Expand Up @@ -114,8 +113,8 @@ impl File {
hasher.update(&self.data[..data_len_without_trailer]);
let actual = hasher
.try_finalize()
.map_err(|e| message!("failed to hash commit graph file: {}", e).raise())?;
actual.verify(self.checksum()).map_err(|e| message!("{}", e).raise())?;
.map_err(|e| message!("failed to hash commit graph file: {e}").raise())?;
actual.verify(self.checksum()).map_err(|e| message!("{e}").raise())?;
Ok(actual)
}
}
Expand Down
4 changes: 1 addition & 3 deletions gix-commitgraph/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ impl Graph {
let num_commits: u64 = files.iter().map(|f| u64::from(f.num_commits())).sum();
if num_commits > u64::from(MAX_COMMITS) {
return Err(message!(
"Commit-graph files contain {} commits altogether, but only {} commits are allowed",
num_commits,
MAX_COMMITS
"Commit-graph files contain {num_commits} commits altogether, but only {MAX_COMMITS} commits are allowed"
));
}

Expand Down
52 changes: 25 additions & 27 deletions gix-commitgraph/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,40 +88,38 @@ impl Graph {
}

let next_file_start_pos = Position(file_start_pos.0 + file.num_commits());
let file_stats = file
.traverse(|commit| {
let mut max_parent_generation = 0u32;
for parent_pos in commit.iter_parents() {
let parent_pos = parent_pos.map_err(|err| err.raise_erased())?;
if parent_pos >= next_file_start_pos {
return Err(message!(
"Commit {} has parent position {parent_pos} that is out of range (should be in range 0-{})",
commit.id(),
Position(next_file_start_pos.0 - 1)
)
.raise_erased());
}
let parent = self.commit_at(parent_pos);
max_parent_generation = max(max_parent_generation, parent.generation());
}

// If the max parent generation is GENERATION_NUMBER_MAX, then this commit's
// generation should be GENERATION_NUMBER_MAX too.
let expected_generation = min(max_parent_generation + 1, GENERATION_NUMBER_MAX);
if commit.generation() != expected_generation {
let file_stats = file.traverse(|commit| {
let mut max_parent_generation = 0u32;
for parent_pos in commit.iter_parents() {
let parent_pos = parent_pos.map_err(|err| err.raise_erased())?;
if parent_pos >= next_file_start_pos {
return Err(message!(
"Commit {}'s generation should be {expected_generation} but is {}",
"Commit {} has parent position {parent_pos} that is out of range (should be in range 0-{})",
commit.id(),
commit.generation()
Position(next_file_start_pos.0 - 1)
)
.raise_erased());
}
let parent = self.commit_at(parent_pos);
max_parent_generation = max(max_parent_generation, parent.generation());
}

// If the max parent generation is GENERATION_NUMBER_MAX, then this commit's
// generation should be GENERATION_NUMBER_MAX too.
let expected_generation = min(max_parent_generation + 1, GENERATION_NUMBER_MAX);
if commit.generation() != expected_generation {
return Err(message!(
"Commit {}'s generation should be {expected_generation} but is {}",
commit.id(),
commit.generation()
)
.raise_erased());
}

processor(commit).or_raise_erased(|| message!("processor failed on commit {id}", id = commit.id()))?;
processor(commit).or_raise_erased(|| message!("processor failed on commit {id}", id = commit.id()))?;

Ok(())
})
.map_err(|err| message!("{}: {}", file.path().display(), err).raise())?;
Ok(())
})?;

max_generation = max(max_generation, file_stats.max_generation);
stats.num_commits += file_stats.num_commits;
Expand Down
25 changes: 25 additions & 0 deletions gix-error/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,34 @@ edition = "2021"
include = ["src/**/*", "LICENSE-*"]
rust-version = "1.82"

[features]
## The [`Exn`](crate::Exn) type converts to [`anyhow::Error`] natively so `?` can be used directly.
##
## Otherwise, it would have to be manually converted via
## [`into_box()`](crate::Exn::into_box()) or [`into_inner()`](crate::Exn::into_inner()).
anyhow = ["dep:anyhow"]
## The [`Error`](crate::Error) type is always flattening the [`Exn`](crate::Exn) error tree
## into a chain of errors, while keeping their locations and runtime type-information.
auto-chain-error = []
## The opposite of `auto-chain-error` and implicitly enabled by default. Use it to override `auto-chain-error`.
tree-error = []

[[test]]
name = "auto-chain-error"
path = "tests/auto_chain_error.rs"
required-features = ["auto-chain-error"]

[dependencies]
bstr = { version = "1.12.0", default-features = false, features = ["std"] }

anyhow = { version = "1.0.100", optional = true }
document-features = { version = "0.2.0", optional = true }

[dev-dependencies]
gix-error = { path = ".", features = ["anyhow"] }
insta = "1.46.0"

[package.metadata.docs.rs]
all-features = true
features = ["document-features"]

36 changes: 36 additions & 0 deletions gix-error/src/concrete/chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::write_location;
use std::fmt::{Debug, Display, Formatter};
use std::panic::Location;

/// A generic error which represents a linked-list of errors and exposes it with [source()](std::error::Error::source).
/// It's meant to be the target of a conversion of any [Exn](crate::Exn) error tree.
///
/// It's useful for inter-op with other error handling crates like `anyhow` which offer simplified access to the error chain,
/// and thus is expected to be wrapped in one of their types intead of being used directly.
pub struct ChainedError {
pub(crate) err: Box<dyn std::error::Error + Send + Sync + 'static>,
pub(crate) location: &'static Location<'static>,
pub(crate) source: Option<Box<ChainedError>>,
}

impl Debug for ChainedError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.err, f)
}
}

impl Display for ChainedError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.err, f)?;
if !f.alternate() {
write_location(f, self.location)?;
}
Ok(())
}
}

impl std::error::Error for ChainedError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source.as_ref().map(|e| e as &(dyn std::error::Error + 'static))
}
}
File renamed without changes.
3 changes: 3 additions & 0 deletions gix-error/src/concrete/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub(super) mod chain;
pub(super) mod message;
pub(super) mod parse;
File renamed without changes.
Loading
Loading