11use super :: { process_changes, Change , Offset , UnblamedHunk } ;
2- use crate :: { BlameEntry , Outcome , Statistics } ;
2+ use crate :: { BlameEntry , Error , Outcome , Statistics } ;
33use gix_diff:: blob:: intern:: TokenSource ;
44use gix_hash:: ObjectId ;
55use 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
331334fn 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
421421fn 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
0 commit comments