Skip to content

Commit 845d96a

Browse files
committed
add Error type
1 parent 4ffe6eb commit 845d96a

File tree

7 files changed

+117
-73
lines changed

7 files changed

+117
-73
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gix-blame/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ gix-hash = { version = "^0.15.0", path = "../gix-hash" }
2121
gix-worktree = { version = "^0.38.0", path = "../gix-worktree", default-features = false, features = ["attributes"] }
2222
gix-traverse = { version = "^0.43.0", path = "../gix-traverse" }
2323

24+
thiserror = "2.0.0"
25+
2426
[dev-dependencies]
2527
gix-ref = { version = "^0.49.0", path = "../gix-ref" }
2628
gix-filter = { version = "^0.16.0", path = "../gix-filter" }

gix-blame/src/error.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use gix_object::bstr::BString;
2+
3+
/// The error returned by [file()](crate::file()).
4+
#[derive(Debug, thiserror::Error)]
5+
#[allow(missing_docs)]
6+
pub enum Error {
7+
#[error("No commit was given")]
8+
EmptyTraversal,
9+
#[error(transparent)]
10+
BlobDiffSetResource(#[from] gix_diff::blob::platform::set_resource::Error),
11+
#[error(transparent)]
12+
BlobDiffPrepare(#[from] gix_diff::blob::platform::prepare_diff::Error),
13+
#[error("The file to blame at '{file_path}' wasn't found in the first commit at {commit_id}")]
14+
FileMissing {
15+
/// The file-path to the object to blame.
16+
file_path: BString,
17+
/// The commit whose tree didn't contain `file_path`.
18+
commit_id: gix_hash::ObjectId,
19+
},
20+
#[error("Couldn't find commit or tree in the object database")]
21+
FindObject(#[from] gix_object::find::Error),
22+
#[error("Could not find existing blob or commit")]
23+
FindExistingObject(#[from] gix_object::find::existing_object::Error),
24+
#[error("Could not find existing iterator over a tree")]
25+
FindExistingIter(#[from] gix_object::find::existing_iter::Error),
26+
#[error("Failed to obtain the next commit in the commit-graph traversal")]
27+
Traverse(#[source] Box<dyn std::error::Error + Send + Sync>),
28+
#[error(transparent)]
29+
DiffTree(#[from] gix_diff::tree::Error),
30+
}

gix-blame/src/file/function.rs

Lines changed: 61 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{process_changes, Change, Offset, UnblamedHunk};
2-
use crate::{BlameEntry, Outcome, Statistics};
2+
use crate::{BlameEntry, Error, Outcome, Statistics};
33
use gix_diff::blob::intern::TokenSource;
44
use gix_hash::ObjectId;
55
use gix_object::{bstr::BStr, FindExt};
@@ -57,18 +57,24 @@ pub fn file<E>(
5757
traverse: impl IntoIterator<Item = Result<gix_traverse::commit::Info, E>>,
5858
resource_cache: &mut gix_diff::blob::Platform,
5959
file_path: &BStr,
60-
) -> Result<Outcome, E> {
60+
) -> Result<Outcome, Error>
61+
where
62+
E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
63+
{
6164
let mut traverse = traverse.into_iter().peekable();
6265
let Some(Ok(suspect)) = traverse.peek().map(|res| res.as_ref().map(|item| item.id)) else {
63-
todo!("return actual error");
66+
return Err(Error::EmptyTraversal);
6467
};
6568
let _span = gix_trace::coarse!("gix_blame::file()", ?file_path, ?suspect);
6669

6770
let mut stats = Statistics::default();
6871
let (mut buf, mut buf2, mut buf3) = (Vec::new(), Vec::new(), Vec::new());
69-
let original_file_entry =
70-
find_path_entry_in_commit(&odb, &suspect, file_path, &mut buf, &mut buf2, &mut stats).unwrap();
71-
let original_file_blob = odb.find_blob(&original_file_entry.oid, &mut buf).unwrap().data.to_vec();
72+
let original_file_entry = find_path_entry_in_commit(&odb, &suspect, file_path, &mut buf, &mut buf2, &mut stats)?
73+
.ok_or_else(|| Error::FileMissing {
74+
file_path: file_path.to_owned(),
75+
commit_id: suspect,
76+
})?;
77+
let original_file_blob = odb.find_blob(&original_file_entry.oid, &mut buf)?.data.to_vec();
7278
let num_lines_in_original = {
7379
let mut interner = gix_diff::blob::intern::Interner::new(original_file_blob.len() / 100);
7480
tokens_for_diffing(&original_file_blob)
@@ -78,15 +84,15 @@ pub fn file<E>(
7884
};
7985

8086
let mut hunks_to_blame = vec![UnblamedHunk::new(
81-
0..num_lines_in_original.try_into().unwrap(),
87+
0..num_lines_in_original as u32,
8288
suspect,
8389
Offset::Added(0),
8490
)];
8591

8692
let mut out = Vec::new();
8793
let mut diff_state = gix_diff::tree::State::default();
8894
'outer: for item in traverse {
89-
let item = item?;
95+
let item = item.map_err(|err| Error::Traverse(err.into()))?;
9096
let suspect = item.id;
9197
stats.commits_traversed += 1;
9298

@@ -107,14 +113,14 @@ pub fn file<E>(
107113
break;
108114
}
109115

110-
let Some(entry) = find_path_entry_in_commit(&odb, &suspect, file_path, &mut buf, &mut buf2, &mut stats) else {
116+
let Some(entry) = find_path_entry_in_commit(&odb, &suspect, file_path, &mut buf, &mut buf2, &mut stats)? else {
111117
continue;
112118
};
113119

114120
if parent_ids.len() == 1 {
115121
let parent_id = parent_ids.pop().expect("just validated there is exactly one");
116122
if let Some(parent_entry) =
117-
find_path_entry_in_commit(&odb, &parent_id, file_path, &mut buf, &mut buf2, &mut stats)
123+
find_path_entry_in_commit(&odb, &parent_id, file_path, &mut buf, &mut buf2, &mut stats)?
118124
{
119125
if entry.oid == parent_entry.oid {
120126
// The blobs storing the blamed file in `entry` and `parent_entry` are identical
@@ -126,7 +132,7 @@ pub fn file<E>(
126132
}
127133
}
128134

129-
let Some(modification) = tree_diff_at_file_path(
135+
let changes_for_file_path = tree_diff_at_file_path(
130136
&odb,
131137
file_path,
132138
item.id,
@@ -136,7 +142,8 @@ pub fn file<E>(
136142
&mut buf,
137143
&mut buf2,
138144
&mut buf3,
139-
) else {
145+
)?;
146+
let Some(modification) = changes_for_file_path else {
140147
// None of the changes affected the file we’re currently blaming. Pass blame to parent.
141148
for unblamed_hunk in &mut hunks_to_blame {
142149
unblamed_hunk.pass_blame(suspect, parent_id);
@@ -159,7 +166,7 @@ pub fn file<E>(
159166
}
160167
gix_diff::tree::recorder::Change::Deletion { .. } => todo!(),
161168
gix_diff::tree::recorder::Change::Modification { previous_oid, oid, .. } => {
162-
let changes = blob_changes(&odb, resource_cache, oid, previous_oid, file_path, &mut stats);
169+
let changes = blob_changes(&odb, resource_cache, oid, previous_oid, file_path, &mut stats)?;
163170
hunks_to_blame = process_changes(&mut out, hunks_to_blame, changes, suspect);
164171
for unblamed_hunk in &mut hunks_to_blame {
165172
unblamed_hunk.pass_blame(suspect, parent_id);
@@ -169,7 +176,7 @@ pub fn file<E>(
169176
} else {
170177
for parent_id in &parent_ids {
171178
if let Some(parent_entry) =
172-
find_path_entry_in_commit(&odb, parent_id, file_path, &mut buf, &mut buf2, &mut stats)
179+
find_path_entry_in_commit(&odb, parent_id, file_path, &mut buf, &mut buf2, &mut stats)?
173180
{
174181
if entry.oid == parent_entry.oid {
175182
// The blobs storing the blamed file in `entry` and `parent_entry` are
@@ -194,7 +201,7 @@ pub fn file<E>(
194201
&mut buf,
195202
&mut buf2,
196203
&mut buf3,
197-
);
204+
)?;
198205
let Some(modification) = changes_for_file_path else {
199206
// None of the changes affected the file we’re currently blaming. Pass blame
200207
// to parent.
@@ -215,7 +222,7 @@ pub fn file<E>(
215222
}
216223
gix_diff::tree::recorder::Change::Deletion { .. } => todo!(),
217224
gix_diff::tree::recorder::Change::Modification { previous_oid, oid, .. } => {
218-
let changes = blob_changes(&odb, resource_cache, oid, previous_oid, file_path, &mut stats);
225+
let changes = blob_changes(&odb, resource_cache, oid, previous_oid, file_path, &mut stats)?;
219226
hunks_to_blame = process_changes(&mut out, hunks_to_blame, changes, suspect);
220227
for unblamed_hunk in &mut hunks_to_blame {
221228
unblamed_hunk.pass_blame(suspect, parent_id);
@@ -300,32 +307,28 @@ fn tree_diff_at_file_path(
300307
commit_buf: &mut Vec<u8>,
301308
lhs_tree_buf: &mut Vec<u8>,
302309
rhs_tree_buf: &mut Vec<u8>,
303-
) -> Option<gix_diff::tree::recorder::Change> {
304-
let parent_tree = odb.find_commit(&parent_id, commit_buf).unwrap().tree();
310+
) -> Result<Option<gix_diff::tree::recorder::Change>, Error> {
311+
let parent_tree = odb.find_commit(&parent_id, commit_buf)?.tree();
305312
stats.commits_to_tree += 1;
306313

307-
let parent_tree_iter = odb
308-
.find(&parent_tree, lhs_tree_buf)
309-
.unwrap()
310-
.try_into_tree_iter()
311-
.unwrap();
314+
let parent_tree_iter = odb.find_tree_iter(&parent_tree, lhs_tree_buf)?;
312315
stats.trees_decoded += 1;
313316

314-
let tree_id = odb.find_commit(&id, commit_buf).unwrap().tree();
317+
let tree_id = odb.find_commit(&id, commit_buf)?.tree();
315318
stats.commits_to_tree += 1;
316319

317-
let tree_iter = odb.find(&tree_id, rhs_tree_buf).unwrap().try_into_tree_iter().unwrap();
320+
let tree_iter = odb.find_tree_iter(&tree_id, rhs_tree_buf)?;
318321
stats.trees_decoded += 1;
319322

320323
let mut recorder = gix_diff::tree::Recorder::default();
321-
gix_diff::tree(parent_tree_iter, tree_iter, state, &odb, &mut recorder).unwrap();
324+
gix_diff::tree(parent_tree_iter, tree_iter, state, &odb, &mut recorder)?;
322325
stats.trees_diffed += 1;
323326

324-
recorder.records.into_iter().find(|change| match change {
327+
Ok(recorder.records.into_iter().find(|change| match change {
325328
gix_diff::tree::recorder::Change::Modification { path, .. } => path == file_path,
326329
gix_diff::tree::recorder::Change::Addition { path, .. } => path == file_path,
327330
gix_diff::tree::recorder::Change::Deletion { path, .. } => path == file_path,
328-
})
331+
}))
329332
}
330333

331334
fn blob_changes(
@@ -335,7 +338,7 @@ fn blob_changes(
335338
previous_oid: ObjectId,
336339
file_path: &BStr,
337340
stats: &mut Statistics,
338-
) -> Vec<Change> {
341+
) -> Result<Vec<Change>, Error> {
339342
/// Record all [`Change`]s to learn about additions, deletions and unchanged portions of a *Blamed File*.
340343
struct ChangeRecorder {
341344
last_seen_after_end: u32,
@@ -387,35 +390,32 @@ fn blob_changes(
387390
}
388391
}
389392

390-
resource_cache
391-
.set_resource(
392-
previous_oid,
393-
gix_object::tree::EntryKind::Blob,
394-
file_path,
395-
gix_diff::blob::ResourceKind::OldOrSource,
396-
&odb,
397-
)
398-
.unwrap();
399-
resource_cache
400-
.set_resource(
401-
oid,
402-
gix_object::tree::EntryKind::Blob,
403-
file_path,
404-
gix_diff::blob::ResourceKind::NewOrDestination,
405-
&odb,
406-
)
407-
.unwrap();
408-
409-
let outcome = resource_cache.prepare_diff().unwrap();
393+
resource_cache.set_resource(
394+
previous_oid,
395+
gix_object::tree::EntryKind::Blob,
396+
file_path,
397+
gix_diff::blob::ResourceKind::OldOrSource,
398+
&odb,
399+
)?;
400+
resource_cache.set_resource(
401+
oid,
402+
gix_object::tree::EntryKind::Blob,
403+
file_path,
404+
gix_diff::blob::ResourceKind::NewOrDestination,
405+
&odb,
406+
)?;
407+
408+
let outcome = resource_cache.prepare_diff()?;
410409
let input = gix_diff::blob::intern::InternedInput::new(
411410
tokens_for_diffing(outcome.old.data.as_slice().unwrap_or_default()),
412411
tokens_for_diffing(outcome.new.data.as_slice().unwrap_or_default()),
413412
);
414413
let number_of_lines_in_destination = input.after.len();
415-
let change_recorder = ChangeRecorder::new(number_of_lines_in_destination.try_into().unwrap());
414+
let change_recorder = ChangeRecorder::new(number_of_lines_in_destination as u32);
416415

416+
let res = gix_diff::blob::diff(gix_diff::blob::Algorithm::Histogram, &input, change_recorder);
417417
stats.blobs_diffed += 1;
418-
gix_diff::blob::diff(gix_diff::blob::Algorithm::Histogram, &input, change_recorder)
418+
Ok(res)
419419
}
420420

421421
fn find_path_entry_in_commit(
@@ -425,19 +425,19 @@ fn find_path_entry_in_commit(
425425
buf: &mut Vec<u8>,
426426
buf2: &mut Vec<u8>,
427427
stats: &mut Statistics,
428-
) -> Option<gix_object::tree::Entry> {
429-
let commit_id = odb.find_commit(commit, buf).unwrap().tree();
430-
let tree_iter = odb.find_tree_iter(&commit_id, buf).unwrap();
428+
) -> Result<Option<gix_object::tree::Entry>, Error> {
429+
let commit_id = odb.find_commit(commit, buf)?.tree();
431430
stats.commits_to_tree += 1;
431+
let tree_iter = odb.find_tree_iter(&commit_id, buf)?;
432432
stats.trees_decoded += 1;
433433

434-
tree_iter
435-
.lookup_entry(
436-
odb,
437-
buf2,
438-
file_path.split(|b| *b == b'/').inspect(|_| stats.trees_decoded += 1),
439-
)
440-
.unwrap()
434+
let res = tree_iter.lookup_entry(
435+
odb,
436+
buf2,
437+
file_path.split(|b| *b == b'/').inspect(|_| stats.trees_decoded += 1),
438+
)?;
439+
stats.trees_decoded -= 1;
440+
Ok(res)
441441
}
442442

443443
/// Return an iterator over tokens for use in diffing. These usually lines, but iit's important to unify them

gix-blame/src/file/mod.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,10 @@ fn process_change(
216216
}
217217
}
218218
(Some(hunk), Some(Change::Deleted(line_number_in_destination, number_of_lines_deleted))) => {
219-
let range_in_suspect = hunk.suspects.get(&suspect).expect("TODO");
219+
let range_in_suspect = hunk
220+
.suspects
221+
.get(&suspect)
222+
.expect("Internal and we know suspect is present");
220223

221224
if line_number_in_destination < range_in_suspect.start {
222225
// <---> (hunk)
@@ -377,7 +380,10 @@ impl UnblamedHunk {
377380
}
378381

379382
fn offset_for(&self, suspect: ObjectId) -> Offset {
380-
let range_in_suspect = self.suspects.get(&suspect).expect("TODO");
383+
let range_in_suspect = self
384+
.suspects
385+
.get(&suspect)
386+
.expect("Internal and we know suspect is present");
381387

382388
if self.range_in_blamed_file.start > range_in_suspect.start {
383389
Offset::Added(self.range_in_blamed_file.start - range_in_suspect.start)
@@ -437,7 +443,10 @@ impl BlameEntry {
437443

438444
/// Create an offset from a portion of the *Original File*.
439445
fn from_unblamed_hunk(unblamed_hunk: &UnblamedHunk, commit_id: ObjectId) -> Self {
440-
let range_in_original_file = unblamed_hunk.suspects.get(&commit_id).unwrap();
446+
let range_in_original_file = unblamed_hunk
447+
.suspects
448+
.get(&commit_id)
449+
.expect("Private and only called when we now `commit_id` is in the suspect list");
441450

442451
Self {
443452
range_in_blamed_file: unblamed_hunk.range_in_blamed_file.clone(),

gix-blame/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#![deny(rust_2018_idioms, missing_docs)]
1515
#![forbid(unsafe_code)]
1616

17+
mod error;
18+
pub use error::Error;
1719
mod types;
1820
pub use types::{BlameEntry, Outcome, Statistics};
1921

0 commit comments

Comments
 (0)