Skip to content

Commit f45cb4b

Browse files
committed
Make clear it's a decode error we are using there
The namespace doesn't look aweome yet though, making it appear it's an 'immutable' error, even though it's a decode error of immmutable objects. immutable::object::decode::Error would be more like it
1 parent 91ee558 commit f45cb4b

File tree

11 files changed

+142
-123
lines changed

11 files changed

+142
-123
lines changed

git-object/src/commit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ where
2929
///
3030
/// A merge tag is a tag object embedded within the respective header field of a commit, making
3131
/// it a child object of sorts.
32-
pub fn mergetags(self) -> impl Iterator<Item = Result<immutable::Tag<'a>, immutable::Error>> {
32+
pub fn mergetags(self) -> impl Iterator<Item = Result<immutable::Tag<'a>, immutable::decode::Error>> {
3333
self.find_all("mergetag").map(|b| immutable::Tag::from_bytes(b))
3434
}
3535

git-object/src/immutable/commit.rs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use super::Error;
21
use crate::{
32
commit,
4-
immutable::{parse, parse::NL, Signature},
3+
immutable::{decode, parse, parse::NL, Signature},
54
BStr, ByteSlice,
65
};
76
use nom::{
@@ -45,8 +44,8 @@ pub struct Commit<'a> {
4544

4645
impl<'a> Commit<'a> {
4746
/// Deserialize a commit from the given `data` bytes while avoiding most allocations.
48-
pub fn from_bytes(data: &'a [u8]) -> Result<Commit<'a>, Error> {
49-
parse(data).map(|(_, t)| t).map_err(Error::from)
47+
pub fn from_bytes(data: &'a [u8]) -> Result<Commit<'a>, decode::Error> {
48+
parse(data).map(|(_, t)| t).map_err(decode::Error::from)
5049
}
5150
/// Return the `tree` fields hash digest.
5251
pub fn tree(&self) -> git_hash::ObjectId {
@@ -66,32 +65,35 @@ impl<'a> Commit<'a> {
6665
}
6766
}
6867

69-
fn parse_message(i: &[u8]) -> IResult<&[u8], &BStr, Error> {
68+
fn parse_message(i: &[u8]) -> IResult<&[u8], &BStr, decode::Error> {
7069
if i.is_empty() {
7170
// newline + [message]
72-
return Err(nom::Err::Error(Error::NomDetail(i.into(), "commit message is missing")));
71+
return Err(nom::Err::Error(decode::Error::NomDetail(
72+
i.into(),
73+
"commit message is missing",
74+
)));
7375
}
74-
let (i, _) = tag(NL)(i).map_err(Error::context("a newline separates headers from the message"))?;
76+
let (i, _) = tag(NL)(i).map_err(decode::Error::context("a newline separates headers from the message"))?;
7577
debug_assert!(!i.is_empty());
7678
Ok((&[], &i.as_bstr()))
7779
}
7880

79-
fn parse(i: &[u8]) -> IResult<&[u8], Commit<'_>, Error> {
80-
let (i, tree) =
81-
parse::header_field(i, b"tree", parse::hex_sha1).map_err(Error::context("tree <40 lowercase hex char>"))?;
81+
fn parse(i: &[u8]) -> IResult<&[u8], Commit<'_>, decode::Error> {
82+
let (i, tree) = parse::header_field(i, b"tree", parse::hex_sha1)
83+
.map_err(decode::Error::context("tree <40 lowercase hex char>"))?;
8284
let (i, parents) = many0(|i| parse::header_field(i, b"parent", parse::hex_sha1))(i)
83-
.map_err(Error::context("zero or more 'parent <40 lowercase hex char>'"))?;
85+
.map_err(decode::Error::context("zero or more 'parent <40 lowercase hex char>'"))?;
8486
let (i, author) =
85-
parse::header_field(i, b"author", parse::signature).map_err(Error::context("author <signature>"))?;
86-
let (i, committer) =
87-
parse::header_field(i, b"committer", parse::signature).map_err(Error::context("committer <signature>"))?;
88-
let (i, encoding) =
89-
opt(|i| parse::header_field(i, b"encoding", is_not(NL)))(i).map_err(Error::context("encoding <encoding>"))?;
87+
parse::header_field(i, b"author", parse::signature).map_err(decode::Error::context("author <signature>"))?;
88+
let (i, committer) = parse::header_field(i, b"committer", parse::signature)
89+
.map_err(decode::Error::context("committer <signature>"))?;
90+
let (i, encoding) = opt(|i| parse::header_field(i, b"encoding", is_not(NL)))(i)
91+
.map_err(decode::Error::context("encoding <encoding>"))?;
9092
let (i, extra_headers) = many0(alt((
9193
|i| parse::any_header_field_multi_line(i).map(|(i, (k, o))| (i, (k.as_bstr(), Cow::Owned(o)))),
9294
|i| parse::any_header_field(i, is_not(NL)).map(|(i, (k, o))| (i, (k.as_bstr(), Cow::Borrowed(o.as_bstr())))),
9395
)))(i)
94-
.map_err(Error::context("<field> <single-line|multi-line>"))?;
96+
.map_err(decode::Error::context("<field> <single-line|multi-line>"))?;
9597
let (i, message) = all_consuming(parse_message)(i)?;
9698

9799
Ok((

git-object/src/immutable/mod.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,69 @@ mod blob;
1717
pub use blob::Blob;
1818

1919
mod object;
20-
pub use object::{Error, Object, Signature};
20+
pub use object::{Object, Signature};
2121

2222
mod parse;
23+
24+
///
25+
pub mod decode {
26+
use nom::error::ParseError;
27+
use quick_error::quick_error;
28+
quick_error! {
29+
/// An error returned by various [`Commit`][crate::immutable::Commit] and [`Signature`][crate::immutable::Signature] methods.
30+
#[derive(Debug)]
31+
#[allow(missing_docs)]
32+
pub enum Error {
33+
ParseIntegerError(msg: &'static str, number: crate::BString, err: btoi::ParseIntegerError) {
34+
display("{}: {:?}", msg, number)
35+
source(err)
36+
}
37+
Nom(err_msg: String) {
38+
display("{}", err_msg)
39+
}
40+
NomDetail(input: crate::BString, msg: &'static str) {
41+
display("{}: '{}' could not be parsed", msg, input)
42+
}
43+
ParseKindError(err: crate::types::Error) {
44+
display("{}", err)
45+
source(err)
46+
}
47+
ObjectKind(err: crate::Error) {
48+
from()
49+
source(err)
50+
}
51+
}
52+
}
53+
54+
impl Error {
55+
fn set_parse_context(mut self, ctx: &'static str) -> Self {
56+
if let Error::NomDetail(_, ref mut message) = self {
57+
*message = ctx
58+
}
59+
self
60+
}
61+
62+
pub(crate) fn context(msg: &'static str) -> impl Fn(nom::Err<Self>) -> nom::Err<Self> {
63+
move |e: nom::Err<Self>| e.map(|e| e.set_parse_context(msg))
64+
}
65+
}
66+
67+
impl ParseError<&[u8]> for Error {
68+
fn from_error_kind(input: &[u8], _kind: nom::error::ErrorKind) -> Self {
69+
Error::NomDetail(input.into(), "parse error")
70+
}
71+
72+
fn append(_: &[u8], _: nom::error::ErrorKind, other: Self) -> Self {
73+
other
74+
}
75+
}
76+
77+
impl From<nom::Err<Error>> for Error {
78+
fn from(e: nom::Err<Error>) -> Self {
79+
match e {
80+
nom::Err::Error(err) | nom::Err::Failure(err) => Error::Nom(err.to_string()),
81+
nom::Err::Incomplete(_) => unreachable!("we do not implement streaming parsers"),
82+
}
83+
}
84+
}
85+
}

git-object/src/immutable/object.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@ use bstr::BStr;
22

33
use crate::{
44
immutable,
5-
immutable::{parse, Blob, Commit, Tag, Tree},
5+
immutable::{decode, parse, Blob, Commit, Tag, Tree},
66
Kind, Time,
77
};
88

9-
mod error;
10-
pub use error::Error;
11-
129
/// A signature is created by an actor at a certain time.
1310
///
1411
/// Note that this is not a cryptographical signature.
@@ -26,8 +23,8 @@ pub struct Signature<'a> {
2623

2724
impl<'a> Signature<'a> {
2825
/// Deserialize a signature from the given `data`.
29-
pub fn from_bytes(data: &'a [u8]) -> Result<Signature<'a>, Error> {
30-
parse::signature(data).map(|(_, t)| t).map_err(Error::from)
26+
pub fn from_bytes(data: &'a [u8]) -> Result<Signature<'a>, decode::Error> {
27+
parse::signature(data).map(|(_, t)| t).map_err(decode::Error::from)
3128
}
3229
}
3330

@@ -45,7 +42,7 @@ pub enum Object<'a> {
4542

4643
impl<'a> Object<'a> {
4744
/// Deserialize an object of `kind` from the given `data`.
48-
pub fn from_bytes(kind: Kind, data: &'a [u8]) -> Result<Object<'a>, Error> {
45+
pub fn from_bytes(kind: Kind, data: &'a [u8]) -> Result<Object<'a>, decode::Error> {
4946
Ok(match kind {
5047
Kind::Tree => Object::Tree(Tree::from_bytes(data)?),
5148
Kind::Blob => Object::Blob(Blob { data }),

git-object/src/immutable/object/error.rs

Lines changed: 0 additions & 59 deletions
This file was deleted.

git-object/src/immutable/parse.rs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
immutable::{Error, Signature},
2+
immutable::{decode, Signature},
33
ByteSlice, Sign, Time,
44
};
55
use bstr::{BStr, BString, ByteVec};
@@ -18,7 +18,7 @@ pub(crate) const NL: &[u8] = b"\n";
1818
pub(crate) const SPACE: &[u8] = b" ";
1919
pub(crate) const SPACE_OR_NL: &[u8] = b" \n";
2020

21-
pub(crate) fn any_header_field_multi_line(i: &[u8]) -> IResult<&[u8], (&[u8], BString), Error> {
21+
pub(crate) fn any_header_field_multi_line(i: &[u8]) -> IResult<&[u8], (&[u8], BString), decode::Error> {
2222
let (i, (k, o)) = peek(tuple((
2323
terminated(is_not(SPACE_OR_NL), tag(SPACE)),
2424
recognize(tuple((
@@ -45,15 +45,15 @@ pub(crate) fn any_header_field_multi_line(i: &[u8]) -> IResult<&[u8], (&[u8], BS
4545
pub(crate) fn header_field<'a, T>(
4646
i: &'a [u8],
4747
name: &'static [u8],
48-
parse_value: impl Fn(&'a [u8]) -> IResult<&'a [u8], T, Error>,
49-
) -> IResult<&'a [u8], T, Error> {
48+
parse_value: impl Fn(&'a [u8]) -> IResult<&'a [u8], T, decode::Error>,
49+
) -> IResult<&'a [u8], T, decode::Error> {
5050
terminated(preceded(terminated(tag(name), tag(SPACE)), parse_value), tag(NL))(i)
5151
}
5252

5353
pub(crate) fn any_header_field<'a, T>(
5454
i: &'a [u8],
55-
parse_value: impl Fn(&'a [u8]) -> IResult<&'a [u8], T, Error>,
56-
) -> IResult<&'a [u8], (&'a [u8], T), Error> {
55+
parse_value: impl Fn(&'a [u8]) -> IResult<&'a [u8], T, decode::Error>,
56+
) -> IResult<&'a [u8], (&'a [u8], T), decode::Error> {
5757
terminated(
5858
tuple((terminated(is_not(SPACE_OR_NL), tag(SPACE)), parse_value)),
5959
tag(NL),
@@ -64,11 +64,11 @@ fn is_hex_digit_lc(b: u8) -> bool {
6464
matches!(b, b'0'..=b'9' | b'a'..=b'f')
6565
}
6666

67-
pub(crate) fn hex_sha1(i: &[u8]) -> IResult<&[u8], &BStr, Error> {
67+
pub(crate) fn hex_sha1(i: &[u8]) -> IResult<&[u8], &BStr, decode::Error> {
6868
take_while_m_n(40usize, 40, is_hex_digit_lc)(i).map(|(i, o)| (i, o.as_bstr()))
6969
}
7070

71-
pub(crate) fn signature(i: &[u8]) -> IResult<&[u8], Signature<'_>, Error> {
71+
pub(crate) fn signature(i: &[u8]) -> IResult<&[u8], Signature<'_>, decode::Error> {
7272
let (i, (name, email, time_in_seconds, tzsign, tzhour, tzminute)) = tuple((
7373
terminated(take_until(&b" <"[..]), take(2usize)),
7474
terminated(take_until(&b"> "[..]), take(2usize)),
@@ -77,15 +77,25 @@ pub(crate) fn signature(i: &[u8]) -> IResult<&[u8], Signature<'_>, Error> {
7777
take_while_m_n(2usize, 2, is_digit),
7878
take_while_m_n(2usize, 2, is_digit),
7979
))(i)
80-
.map_err(Error::context(
80+
.map_err(decode::Error::context(
8181
"tagger <name> <<email>> <time seconds since epoch> <+|-><HHMM>",
8282
))?;
8383

8484
let sign = if tzsign[0] == b'-' { Sign::Minus } else { Sign::Plus };
85-
let hours = btoi::<i32>(&tzhour)
86-
.map_err(|e| nom::Err::Error(Error::ParseIntegerError("invalid 'hours' string", tzhour.into(), e)))?;
87-
let minutes = btoi::<i32>(&tzminute)
88-
.map_err(|e| nom::Err::Error(Error::ParseIntegerError("invalid 'minutes' string", tzminute.into(), e)))?;
85+
let hours = btoi::<i32>(&tzhour).map_err(|e| {
86+
nom::Err::Error(decode::Error::ParseIntegerError(
87+
"invalid 'hours' string",
88+
tzhour.into(),
89+
e,
90+
))
91+
})?;
92+
let minutes = btoi::<i32>(&tzminute).map_err(|e| {
93+
nom::Err::Error(decode::Error::ParseIntegerError(
94+
"invalid 'minutes' string",
95+
tzminute.into(),
96+
e,
97+
))
98+
})?;
8999
let offset = (hours * 3600 + minutes * 60) * if sign == Sign::Minus { -1 } else { 1 };
90100

91101
Ok((
@@ -95,7 +105,7 @@ pub(crate) fn signature(i: &[u8]) -> IResult<&[u8], Signature<'_>, Error> {
95105
email: email.as_bstr(),
96106
time: Time {
97107
time: btoi::<u32>(time_in_seconds).map_err(|e| {
98-
nom::Err::Error(Error::ParseIntegerError(
108+
nom::Err::Error(decode::Error::ParseIntegerError(
99109
"Could parse to seconds",
100110
time_in_seconds.into(),
101111
e,

0 commit comments

Comments
 (0)