Skip to content

Commit c372321

Browse files
committed
Merge branch 'error'
2 parents ff99a18 + 0ed0a89 commit c372321

File tree

18 files changed

+117
-86
lines changed

18 files changed

+117
-86
lines changed

Cargo.lock

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

gix-command/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ include = ["src/lib.rs", "LICENSE-*"]
1313
doctest = false
1414

1515
[dependencies]
16+
gix-trace = { version = "^0.1.3", path = "../gix-trace" }
17+
1618
bstr = { version = "1.5.0", default-features = false, features = ["std"] }
1719

1820
[dev-dependencies]

gix-command/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ mod prepare {
9393
impl Prepare {
9494
/// Spawn the command as configured.
9595
pub fn spawn(self) -> std::io::Result<std::process::Child> {
96-
Command::from(self).spawn()
96+
let mut cmd = Command::from(self);
97+
gix_trace::debug!(cmd = ?cmd);
98+
cmd.spawn()
9799
}
98100
}
99101

gix-credentials/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ gix-path = { version = "^0.10.0", path = "../gix-path" }
2323
gix-command = { version = "^0.2.10", path = "../gix-command" }
2424
gix-config-value = { version = "^0.14.0", path = "../gix-config-value" }
2525
gix-prompt = { version = "^0.7.0", path = "../gix-prompt" }
26+
gix-trace = { version = "^0.1.3", path = "../gix-trace" }
2627

2728
thiserror = "1.0.32"
2829
serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] }

gix-credentials/src/program/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ impl Program {
106106
Stdio::null()
107107
})
108108
.stderr(if self.stderr { Stdio::inherit() } else { Stdio::null() });
109+
gix_trace::debug!(cmd = ?cmd, "launching credential helper");
109110
let mut child = cmd.spawn()?;
110111
let stdin = child.stdin.take().expect("stdin to be configured");
111112
let stdout = child.stdout.take();

gix-object/src/commit/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::ops::Range;
22

33
use bstr::{BStr, BString, ByteSlice};
4+
use winnow::prelude::*;
45

56
use crate::{Commit, CommitRef, TagRef};
67

@@ -60,7 +61,11 @@ mod write;
6061
impl<'a> CommitRef<'a> {
6162
/// Deserialize a commit from the given `data` bytes while avoiding most allocations.
6263
pub fn from_bytes(mut data: &'a [u8]) -> Result<CommitRef<'a>, crate::decode::Error> {
63-
decode::commit(&mut data).map_err(crate::decode::Error::with_err)
64+
let input = &mut data;
65+
match decode::commit.parse_next(input) {
66+
Ok(tag) => Ok(tag),
67+
Err(err) => Err(crate::decode::Error::with_err(err, input)),
68+
}
6469
}
6570
}
6671

gix-object/src/commit/ref_iter.rs

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -154,44 +154,42 @@ fn missing_field() -> crate::decode::Error {
154154

155155
impl<'a> CommitRefIter<'a> {
156156
#[inline]
157-
fn next_inner(i: &'a [u8], state: &mut State) -> Result<(&'a [u8], Token<'a>), crate::decode::Error> {
158-
Self::next_inner_(i, state).map_err(crate::decode::Error::with_err)
157+
fn next_inner(mut i: &'a [u8], state: &mut State) -> Result<(&'a [u8], Token<'a>), crate::decode::Error> {
158+
let input = &mut i;
159+
match Self::next_inner_(input, state) {
160+
Ok(token) => Ok((*input, token)),
161+
Err(err) => Err(crate::decode::Error::with_err(err, input)),
162+
}
159163
}
160164

161165
fn next_inner_(
162-
mut i: &'a [u8],
166+
input: &mut &'a [u8],
163167
state: &mut State,
164-
) -> Result<(&'a [u8], Token<'a>), winnow::error::ErrMode<crate::decode::ParseError>> {
168+
) -> Result<Token<'a>, winnow::error::ErrMode<crate::decode::ParseError>> {
165169
use State::*;
166170
Ok(match state {
167171
Tree => {
168172
let tree = (|i: &mut _| parse::header_field(i, b"tree", parse::hex_hash))
169173
.context(StrContext::Expected("tree <40 lowercase hex char>".into()))
170-
.parse_next(&mut i)?;
174+
.parse_next(input)?;
171175
*state = State::Parents;
172-
(
173-
i,
174-
Token::Tree {
175-
id: ObjectId::from_hex(tree).expect("parsing validation"),
176-
},
177-
)
176+
Token::Tree {
177+
id: ObjectId::from_hex(tree).expect("parsing validation"),
178+
}
178179
}
179180
Parents => {
180181
let parent = opt(|i: &mut _| parse::header_field(i, b"parent", parse::hex_hash))
181182
.context(StrContext::Expected("commit <40 lowercase hex char>".into()))
182-
.parse_next(&mut i)?;
183+
.parse_next(input)?;
183184
match parent {
184-
Some(parent) => (
185-
i,
186-
Token::Parent {
187-
id: ObjectId::from_hex(parent).expect("parsing validation"),
188-
},
189-
),
185+
Some(parent) => Token::Parent {
186+
id: ObjectId::from_hex(parent).expect("parsing validation"),
187+
},
190188
None => {
191189
*state = State::Signature {
192190
of: SignatureKind::Author,
193191
};
194-
return Self::next_inner_(i, state);
192+
Self::next_inner_(input, state)?
195193
}
196194
}
197195
}
@@ -209,23 +207,20 @@ impl<'a> CommitRefIter<'a> {
209207
};
210208
let signature = (|i: &mut _| parse::header_field(i, field_name, parse::signature))
211209
.context(StrContext::Expected(err_msg.into()))
212-
.parse_next(&mut i)?;
213-
(
214-
i,
215-
match who {
216-
SignatureKind::Author => Token::Author { signature },
217-
SignatureKind::Committer => Token::Committer { signature },
218-
},
219-
)
210+
.parse_next(input)?;
211+
match who {
212+
SignatureKind::Author => Token::Author { signature },
213+
SignatureKind::Committer => Token::Committer { signature },
214+
}
220215
}
221216
Encoding => {
222217
let encoding = opt(|i: &mut _| parse::header_field(i, b"encoding", take_till1(NL)))
223218
.context(StrContext::Expected("encoding <encoding>".into()))
224-
.parse_next(&mut i)?;
219+
.parse_next(input)?;
225220
*state = State::ExtraHeaders;
226221
match encoding {
227-
Some(encoding) => (i, Token::Encoding(encoding.as_bstr())),
228-
None => return Self::next_inner_(i, state),
222+
Some(encoding) => Token::Encoding(encoding.as_bstr()),
223+
None => Self::next_inner_(input, state)?,
229224
}
230225
}
231226
ExtraHeaders => {
@@ -237,22 +232,22 @@ impl<'a> CommitRefIter<'a> {
237232
},
238233
)))
239234
.context(StrContext::Expected("<field> <single-line|multi-line>".into()))
240-
.parse_next(&mut i)?;
235+
.parse_next(input)?;
241236
match extra_header {
242-
Some(extra_header) => (i, Token::ExtraHeader(extra_header)),
237+
Some(extra_header) => Token::ExtraHeader(extra_header),
243238
None => {
244239
*state = State::Message;
245-
return Self::next_inner_(i, state);
240+
Self::next_inner_(input, state)?
246241
}
247242
}
248243
}
249244
Message => {
250-
let message = terminated(decode::message, eof).parse_next(&mut i)?;
245+
let message = terminated(decode::message, eof).parse_next(input)?;
251246
debug_assert!(
252-
i.is_empty(),
247+
input.is_empty(),
253248
"we should have consumed all data - otherwise iter may go forever"
254249
);
255-
return Ok((i, Token::Message(message)));
250+
Token::Message(message)
256251
}
257252
})
258253
}

gix-object/src/lib.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ pub mod decode {
267267
pub(crate) fn empty_error() -> Error {
268268
Error {
269269
inner: winnow::error::ContextError::new(),
270+
remaining: Default::default(),
270271
}
271272
}
272273

@@ -275,19 +276,27 @@ pub mod decode {
275276
pub struct Error {
276277
/// The actual error
277278
pub inner: ParseError,
279+
/// Where the error occurred
280+
pub remaining: Vec<u8>,
278281
}
279282

280283
impl Error {
281-
pub(crate) fn with_err(err: winnow::error::ErrMode<ParseError>) -> Self {
284+
pub(crate) fn with_err(err: winnow::error::ErrMode<ParseError>, remaining: &[u8]) -> Self {
282285
Self {
283286
inner: err.into_inner().expect("we don't have streaming parsers"),
287+
remaining: remaining.to_owned(),
284288
}
285289
}
286290
}
287291

288292
impl std::fmt::Display for Error {
289293
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290-
self.inner.fmt(f)
294+
write!(f, "object parsing failed at `{}`", bstr::BStr::new(&self.remaining))?;
295+
if self.inner.context().next().is_some() {
296+
writeln!(f)?;
297+
self.inner.fmt(f)?;
298+
}
299+
Ok(())
291300
}
292301
}
293302

@@ -316,7 +325,7 @@ pub mod decode {
316325
}
317326

318327
impl Error {
319-
pub(crate) fn with_err(err: winnow::error::ErrMode<ParseError>) -> Self {
328+
pub(crate) fn with_err(err: winnow::error::ErrMode<ParseError>, _remaining: &[u8]) -> Self {
320329
Self {
321330
inner: err.into_inner().expect("we don't have streaming parsers"),
322331
}

gix-object/src/tag/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use winnow::prelude::*;
2+
13
use crate::TagRef;
24

35
mod decode;
@@ -11,7 +13,11 @@ pub mod ref_iter;
1113
impl<'a> TagRef<'a> {
1214
/// Deserialize a tag from `data`.
1315
pub fn from_bytes(mut data: &'a [u8]) -> Result<TagRef<'a>, crate::decode::Error> {
14-
decode::git_tag(&mut data).map_err(crate::decode::Error::with_err)
16+
let input = &mut data;
17+
match decode::git_tag.parse_next(input) {
18+
Ok(tag) => Ok(tag),
19+
Err(err) => Err(crate::decode::Error::with_err(err, input)),
20+
}
1521
}
1622
/// The object this tag points to as `Id`.
1723
pub fn target(&self) -> gix_hash::ObjectId {

gix-object/src/tag/ref_iter.rs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,58 +59,59 @@ fn missing_field() -> crate::decode::Error {
5959

6060
impl<'a> TagRefIter<'a> {
6161
#[inline]
62-
fn next_inner(i: &'a [u8], state: &mut State) -> Result<(&'a [u8], Token<'a>), crate::decode::Error> {
63-
Self::next_inner_(i, state).map_err(crate::decode::Error::with_err)
62+
fn next_inner(mut i: &'a [u8], state: &mut State) -> Result<(&'a [u8], Token<'a>), crate::decode::Error> {
63+
let input = &mut i;
64+
match Self::next_inner_(input, state) {
65+
Ok(token) => Ok((*input, token)),
66+
Err(err) => Err(crate::decode::Error::with_err(err, input)),
67+
}
6468
}
6569

6670
fn next_inner_(
67-
mut i: &'a [u8],
71+
input: &mut &'a [u8],
6872
state: &mut State,
69-
) -> Result<(&'a [u8], Token<'a>), winnow::error::ErrMode<crate::decode::ParseError>> {
73+
) -> Result<Token<'a>, winnow::error::ErrMode<crate::decode::ParseError>> {
7074
use State::*;
7175
Ok(match state {
7276
Target => {
7377
let target = (|i: &mut _| parse::header_field(i, b"object", parse::hex_hash))
7478
.context(StrContext::Expected("object <40 lowercase hex char>".into()))
75-
.parse_next(&mut i)?;
79+
.parse_next(input)?;
7680
*state = TargetKind;
77-
(
78-
i,
79-
Token::Target {
80-
id: ObjectId::from_hex(target).expect("parsing validation"),
81-
},
82-
)
81+
Token::Target {
82+
id: ObjectId::from_hex(target).expect("parsing validation"),
83+
}
8384
}
8485
TargetKind => {
8586
let kind = (|i: &mut _| parse::header_field(i, b"type", take_while(1.., AsChar::is_alpha)))
8687
.context(StrContext::Expected("type <object kind>".into()))
87-
.parse_next(&mut i)?;
88+
.parse_next(input)?;
8889
let kind = Kind::from_bytes(kind)
89-
.map_err(|_| winnow::error::ErrMode::from_error_kind(&i, winnow::error::ErrorKind::Verify))?;
90+
.map_err(|_| winnow::error::ErrMode::from_error_kind(input, winnow::error::ErrorKind::Verify))?;
9091
*state = Name;
91-
(i, Token::TargetKind(kind))
92+
Token::TargetKind(kind)
9293
}
9394
Name => {
9495
let tag_version = (|i: &mut _| parse::header_field(i, b"tag", take_while(1.., |b| b != NL[0])))
9596
.context(StrContext::Expected("tag <version>".into()))
96-
.parse_next(&mut i)?;
97+
.parse_next(input)?;
9798
*state = Tagger;
98-
(i, Token::Name(tag_version.as_bstr()))
99+
Token::Name(tag_version.as_bstr())
99100
}
100101
Tagger => {
101102
let signature = opt(|i: &mut _| parse::header_field(i, b"tagger", parse::signature))
102103
.context(StrContext::Expected("tagger <signature>".into()))
103-
.parse_next(&mut i)?;
104+
.parse_next(input)?;
104105
*state = Message;
105-
(i, Token::Tagger(signature))
106+
Token::Tagger(signature)
106107
}
107108
Message => {
108-
let (message, pgp_signature) = terminated(decode::message, eof).parse_next(&mut i)?;
109+
let (message, pgp_signature) = terminated(decode::message, eof).parse_next(input)?;
109110
debug_assert!(
110-
i.is_empty(),
111+
input.is_empty(),
111112
"we should have consumed all data - otherwise iter may go forever"
112113
);
113-
return Ok((i, Token::Body { message, pgp_signature }));
114+
Token::Body { message, pgp_signature }
114115
}
115116
})
116117
}

gix-object/src/tree/ref_iter.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::convert::TryFrom;
22

33
use bstr::BStr;
44
use winnow::error::ParserError;
5+
use winnow::prelude::*;
56

67
use crate::{tree, tree::EntryRef, TreeRef, TreeRefIter};
78

@@ -15,7 +16,11 @@ impl<'a> TreeRefIter<'a> {
1516
impl<'a> TreeRef<'a> {
1617
/// Deserialize a Tree from `data`.
1718
pub fn from_bytes(mut data: &'a [u8]) -> Result<TreeRef<'a>, crate::decode::Error> {
18-
decode::tree(&mut data).map_err(crate::decode::Error::with_err)
19+
let input = &mut data;
20+
match decode::tree.parse_next(input) {
21+
Ok(tag) => Ok(tag),
22+
Err(err) => Err(crate::decode::Error::with_err(err, input)),
23+
}
1924
}
2025

2126
/// Find an entry named `name` knowing if the entry is a directory or not, using a binary search.
@@ -73,6 +78,7 @@ impl<'a> Iterator for TreeRefIter<'a> {
7378
#[allow(clippy::unit_arg)]
7479
Some(Err(crate::decode::Error::with_err(
7580
winnow::error::ErrMode::from_error_kind(&failing, winnow::error::ErrorKind::Verify),
81+
failing,
7682
)))
7783
}
7884
}

gix-object/tests/commit/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ fn invalid() {
169169
assert_eq!(
170170
CommitRef::from_bytes(partial_commit).unwrap_err().to_string(),
171171
if cfg!(feature = "verbose-object-parsing-errors") {
172-
"expected `<timestamp>`, `<name> <<email>> <timestamp> <+|-><HHMM>`, `author <signature>`"
172+
"object parsing failed at `1`\nexpected `<timestamp>`, `<name> <<email>> <timestamp> <+|-><HHMM>`, `author <signature>`"
173173
} else {
174174
"object parsing failed"
175175
}

gix-object/tests/tag/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ fn invalid() {
123123
assert_eq!(
124124
TagRef::from_bytes(partial_tag).unwrap_err().to_string(),
125125
if cfg!(feature = "verbose-object-parsing-errors") {
126-
""
126+
"object parsing failed at `tagger Sebasti`"
127127
} else {
128128
"object parsing failed"
129129
}

0 commit comments

Comments
 (0)