Skip to content

Commit b94471a

Browse files
committed
Full error handling for CommitRefIter (#364)
1 parent 6129607 commit b94471a

File tree

12 files changed

+66
-44
lines changed

12 files changed

+66
-44
lines changed

experiments/traversal/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ where
209209
for commit in commits {
210210
let tree_id = db
211211
.try_find(commit, &mut buf)?
212-
.and_then(|o| o.try_into_commit_iter().and_then(|mut c| c.tree_id()))
212+
.and_then(|o| o.try_into_commit_iter().and_then(|mut c| c.tree_id().ok()))
213213
.expect("commit as starting point");
214214

215215
let mut count = Count { entries: 0, seen };

git-actor/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub struct Signature {
3232
/// A immutable signature is created by an actor at a certain time.
3333
///
3434
/// Note that this is not a cryptographical signature.
35-
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
35+
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy, Default)]
3636
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
3737
pub struct SignatureRef<'a> {
3838
/// The actor's name.

git-object/src/commit/ref_iter.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ impl<'a> CommitRefIter<'a> {
4949
/// Errors are coerced into options, hiding whether there was an error or not. The caller should assume an error if they
5050
/// call the method as intended. Such a squelched error cannot be recovered unless the objects data is retrieved and parsed again.
5151
/// `next()`.
52-
pub fn tree_id(&mut self) -> Option<ObjectId> {
53-
self.next().and_then(Result::ok).and_then(Token::try_into_id)
52+
pub fn tree_id(&mut self) -> Result<ObjectId, crate::decode::Error> {
53+
let tree_id = self.next().ok_or_else(missing_field)??;
54+
Token::try_into_id(tree_id).ok_or_else(missing_field)
5455
}
5556

5657
/// Return all parent_ids as iterator.
@@ -77,32 +78,43 @@ impl<'a> CommitRefIter<'a> {
7778

7879
/// Returns the committer signature if there is no decoding error.
7980
/// Errors are coerced into options, hiding whether there was an error or not. The caller knows if there was an error or not.
80-
pub fn committer(mut self) -> Option<git_actor::SignatureRef<'a>> {
81+
pub fn committer(mut self) -> Result<git_actor::SignatureRef<'a>, crate::decode::Error> {
8182
self.find_map(|t| match t {
82-
Ok(Token::Committer { signature }) => Some(signature),
83+
Ok(Token::Committer { signature }) => Some(Ok(signature)),
84+
Err(err) => Some(Err(err)),
8385
_ => None,
8486
})
87+
.ok_or_else(missing_field)?
8588
}
8689

8790
/// Returns the author signature if there is no decoding error.
8891
/// Errors are coerced into options, hiding whether there was an error or not. The caller knows if there was an error or not.
89-
pub fn author(mut self) -> Option<git_actor::SignatureRef<'a>> {
92+
pub fn author(mut self) -> Result<git_actor::SignatureRef<'a>, crate::decode::Error> {
9093
self.find_map(|t| match t {
91-
Ok(Token::Author { signature }) => Some(signature),
94+
Ok(Token::Author { signature }) => Some(Ok(signature)),
95+
Err(err) => Some(Err(err)),
9296
_ => None,
9397
})
98+
.ok_or_else(missing_field)?
9499
}
95100

96101
/// Returns the message if there is no decoding error.
97102
/// Errors are coerced into options, hiding whether there was an error or not. The caller knows if there was an error or not.
98-
pub fn message(mut self) -> Option<&'a BStr> {
103+
pub fn message(mut self) -> Result<&'a BStr, crate::decode::Error> {
99104
self.find_map(|t| match t {
100-
Ok(Token::Message(msg)) => Some(msg),
105+
Ok(Token::Message(msg)) => Some(Ok(msg)),
106+
Err(err) => Some(Err(err)),
101107
_ => None,
102108
})
109+
.transpose()
110+
.map(|msg| msg.unwrap_or_default())
103111
}
104112
}
105113

114+
fn missing_field() -> crate::decode::Error {
115+
crate::decode::empty_error()
116+
}
117+
106118
impl<'a> CommitRefIter<'a> {
107119
fn next_inner(i: &'a [u8], state: &mut State) -> Result<(&'a [u8], Token<'a>), crate::decode::Error> {
108120
use State::*;

git-object/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,12 @@ pub mod decode {
253253
/// The owned type to be used for parse errors.
254254
pub type ParseErrorOwned = nom::error::VerboseError<BString>;
255255

256+
pub(crate) fn empty_error() -> Error {
257+
Error {
258+
inner: nom::error::VerboseError::<BString> { errors: Vec::new() },
259+
}
260+
}
261+
256262
/// A type to indicate errors during parsing and to abstract away details related to `nom`.
257263
#[derive(Debug, Clone)]
258264
pub struct Error {
@@ -292,6 +298,10 @@ pub mod decode {
292298
/// The owned type to be used for parse errors, discards everything and is zero size
293299
pub type ParseErrorOwned = ();
294300

301+
pub(crate) fn empty_error() -> Error {
302+
Error { inner: () }
303+
}
304+
295305
/// A type to indicate errors during parsing and to abstract away details related to `nom`.
296306
#[derive(Debug, Clone)]
297307
pub struct Error {
@@ -316,6 +326,7 @@ pub mod decode {
316326
}
317327
}
318328
}
329+
pub(crate) use _decode::empty_error;
319330
pub use _decode::{Error, ParseError, ParseErrorOwned};
320331
impl std::error::Error for Error {}
321332

git-object/src/tag/ref_iter.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,16 @@ impl<'a> TagRefIter<'a> {
3939
/// Errors are coerced into options, hiding whether there was an error or not. The caller should assume an error if they
4040
/// call the method as intended. Such a squelched error cannot be recovered unless the objects data is retrieved and parsed again.
4141
/// `next()`.
42-
pub fn target_id(&mut self) -> Option<ObjectId> {
43-
self.next().and_then(Result::ok).and_then(Token::into_id)
42+
pub fn target_id(&mut self) -> Result<ObjectId, crate::decode::Error> {
43+
let token = self.next().ok_or_else(missing_field)??;
44+
Token::into_id(token).ok_or_else(missing_field)
4445
}
4546
}
4647

48+
fn missing_field() -> crate::decode::Error {
49+
crate::decode::empty_error()
50+
}
51+
4752
impl<'a> TagRefIter<'a> {
4853
fn next_inner(i: &'a [u8], state: &mut State) -> Result<(&'a [u8], Token<'a>), crate::decode::Error> {
4954
use State::*;

git-object/tests/immutable/commit/iter.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ fn newline_right_after_signature_multiline_header() -> crate::Result {
2525
#[test]
2626
fn signed_with_encoding() -> crate::Result {
2727
let input = fixture_bytes("commit", "signed-with-encoding.txt");
28-
let mut iter = CommitRefIter::from_bytes(&input);
28+
let iter = CommitRefIter::from_bytes(&input);
2929
assert_eq!(
3030
iter.collect::<Result<Vec<_>, _>>()?,
3131
vec![
@@ -47,8 +47,8 @@ fn signed_with_encoding() -> crate::Result {
4747
]
4848
);
4949

50-
assert_eq!(iter.author(), Some(signature(1592448995)));
51-
assert_eq!(iter.committer(), Some(signature(1592449083)));
50+
assert_eq!(iter.author().ok(), Some(signature(1592448995)));
51+
assert_eq!(iter.committer().ok(), Some(signature(1592449083)));
5252
Ok(())
5353
}
5454

@@ -140,7 +140,7 @@ fn error_handling() -> crate::Result {
140140
#[test]
141141
fn mergetag() -> crate::Result {
142142
let input = fixture_bytes("commit", "mergetag.txt");
143-
let mut iter = CommitRefIter::from_bytes(&input);
143+
let iter = CommitRefIter::from_bytes(&input);
144144
assert_eq!(
145145
iter.collect::<Result<Vec<_>, _>>()?,
146146
vec![
@@ -170,7 +170,7 @@ fn mergetag() -> crate::Result {
170170
hex_to_id("8d485da0ddee79d0e6713405694253d401e41b93")
171171
]
172172
);
173-
assert_eq!(iter.message(), Some(LONG_MESSAGE.into()));
173+
assert_eq!(iter.message().ok(), Some(LONG_MESSAGE.into()));
174174
Ok(())
175175
}
176176

@@ -187,7 +187,7 @@ mod method {
187187
let input = fixture_bytes("commit", "unsigned.txt");
188188
let iter = CommitRefIter::from_bytes(&input);
189189
assert_eq!(
190-
iter.clone().tree_id(),
190+
iter.clone().tree_id().ok(),
191191
Some(hex_to_id("1b2dfb4ac5e42080b682fc676e9738c94ce6d54d"))
192192
);
193193
assert_eq!(
@@ -201,14 +201,14 @@ mod method {
201201
#[test]
202202
fn signatures() -> crate::Result {
203203
let input = fixture_bytes("commit", "unsigned.txt");
204-
let mut iter = CommitRefIter::from_bytes(&input);
204+
let iter = CommitRefIter::from_bytes(&input);
205205
assert_eq!(
206206
iter.signatures().collect::<Vec<_>>(),
207207
vec![signature(1592437401), signature(1592437401)]
208208
);
209-
assert_eq!(iter.author(), Some(signature(1592437401)));
210-
assert_eq!(iter.committer(), Some(signature(1592437401)));
211-
assert_eq!(iter.committer(), None, "it's consuming");
209+
assert_eq!(iter.author().ok(), Some(signature(1592437401)));
210+
assert_eq!(iter.committer().ok(), Some(signature(1592437401)));
211+
assert_eq!(iter.author().ok(), Some(signature(1592437401)), "it's not consuming");
212212
Ok(())
213213
}
214214
}

git-ref/src/store/file/raw_ext.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ impl ReferenceExt for Reference {
118118
})?;
119119
match kind {
120120
git_object::Kind::Tag => {
121-
oid = git_object::TagRefIter::from_bytes(data).target_id().ok_or_else(|| {
121+
oid = git_object::TagRefIter::from_bytes(data).target_id().map_err(|_err| {
122122
peel::to_id::Error::NotFound {
123123
oid,
124124
name: self.name.0.clone(),

git-ref/src/store/packed/transaction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ impl packed::Transaction {
6868
let kind = find(next_id, &mut buf)?;
6969
match kind {
7070
Some(kind) if kind == git_object::Kind::Tag => {
71-
next_id = git_object::TagRefIter::from_bytes(&buf).target_id().ok_or_else(|| {
71+
next_id = git_object::TagRefIter::from_bytes(&buf).target_id().map_err(|_| {
7272
prepare::Error::Resolve(
7373
format!("Couldn't get target object id from tag {}", next_id).into(),
7474
)

git-repository/src/object/commit.rs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod error {
99
#[error(transparent)]
1010
FindExistingObject(#[from] object::find::existing::OdbError),
1111
#[error("The commit could not be decoded fully or partially")]
12-
Decode,
12+
Decode(#[from] git_object::decode::Error),
1313
#[error("Expected object of type {}, but got {}", .expected, .actual)]
1414
ObjectKind {
1515
expected: git_object::Kind,
@@ -46,14 +46,12 @@ impl<'repo> Commit<'repo> {
4646
}
4747

4848
/// Parse the commits message into a [`MessageRef`][git_object::commit::MessageRef]
49-
pub fn message(&self) -> Result<git_object::commit::MessageRef<'_>, Error> {
49+
pub fn message(&self) -> Result<git_object::commit::MessageRef<'_>, git_object::decode::Error> {
5050
Ok(git_object::commit::MessageRef::from_bytes(self.message_raw()?))
5151
}
5252
/// Decode the entire commit object in full and return the raw message bytes.
53-
pub fn message_raw(&self) -> Result<&'_ BStr, Error> {
54-
git_object::CommitRefIter::from_bytes(&self.data)
55-
.message()
56-
.ok_or(Error::Decode)
53+
pub fn message_raw(&self) -> Result<&'_ BStr, git_object::decode::Error> {
54+
git_object::CommitRefIter::from_bytes(&self.data).message()
5755
}
5856
/// Decode the commit and obtain the time at which the commit was created.
5957
///
@@ -79,17 +77,13 @@ impl<'repo> Commit<'repo> {
7977
}
8078

8179
/// Return the commits author.
82-
pub fn author(&self) -> Result<git_actor::SignatureRef<'_>, Error> {
83-
git_object::CommitRefIter::from_bytes(&self.data)
84-
.author()
85-
.ok_or(Error::Decode)
80+
pub fn author(&self) -> Result<git_actor::SignatureRef<'_>, git_object::decode::Error> {
81+
git_object::CommitRefIter::from_bytes(&self.data).author()
8682
}
8783

8884
/// Return the commits committer.
89-
pub fn committer(&self) -> Result<git_actor::SignatureRef<'_>, Error> {
90-
git_object::CommitRefIter::from_bytes(&self.data)
91-
.committer()
92-
.ok_or(Error::Decode)
85+
pub fn committer(&self) -> Result<git_actor::SignatureRef<'_>, git_object::decode::Error> {
86+
git_object::CommitRefIter::from_bytes(&self.data).committer()
9387
}
9488

9589
/// Decode this commits parent ids on the fly without allocating.
@@ -104,15 +98,15 @@ impl<'repo> Commit<'repo> {
10498

10599
/// Parse the commit and return the the tree object it points to.
106100
pub fn tree(&self) -> Result<Tree<'repo>, Error> {
107-
let tree_id = self.tree_id().ok_or(Error::Decode)?;
101+
let tree_id = self.tree_id()?;
108102
match self.repo.find_object(tree_id)?.try_into_tree() {
109103
Ok(tree) => Ok(tree),
110104
Err(crate::object::try_into::Error { actual, expected, .. }) => Err(Error::ObjectKind { actual, expected }),
111105
}
112106
}
113107

114108
/// Parse the commit and return the the tree id it points to.
115-
pub fn tree_id(&self) -> Option<git_hash::ObjectId> {
109+
pub fn tree_id(&self) -> Result<git_hash::ObjectId, git_object::decode::Error> {
116110
git_object::CommitRefIter::from_bytes(&self.data).tree_id()
117111
}
118112

git-repository/tests/easy/object.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ mod commit {
2121

2222
assert_eq!(commit.tree()?.id, commit.tree_id().expect("id present"));
2323
assert_eq!(
24-
commit.tree_id(),
24+
commit.tree_id().ok(),
2525
Some(hex_to_id("21d3ba9a26b790a4858d67754ae05d04dfce4d0c"))
2626
);
2727
Ok(())

git-traverse/src/commit.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ pub mod ancestors {
214214
Ok(git_object::commit::ref_iter::Token::Parent { id }) => {
215215
let parent = (self.find)(id.as_ref(), &mut state.parents_buf).ok();
216216

217-
let parent_committer_date =
218-
parent.and_then(|parent| parent.committer().map(|committer| committer.time));
217+
let parent_committer_date = parent
218+
.and_then(|parent| parent.committer().ok().map(|committer| committer.time));
219219

220220
if let Some(parent_committer_date) = parent_committer_date {
221221
state

gitoxide-core/src/hours.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ where
8989
objs::CommitRefIter::from_bytes(&commit_data)
9090
.signatures()
9191
.next()
92-
.map(|author| mailmap.resolve(&author))
92+
.map(|author| mailmap.resolve(author))
9393
})
9494
.try_fold(
9595
|| Vec::new(),

0 commit comments

Comments
 (0)