Skip to content

Commit ebdf205

Browse files
committed
implement parse_signature with nom, starting to like it
1 parent 5b43270 commit ebdf205

File tree

3 files changed

+84
-32
lines changed

3 files changed

+84
-32
lines changed

git-object/src/parsed/tag.rs

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,36 @@ use super::{
22
util::{parse_timezone_offset, split2_at_space},
33
Error,
44
};
5-
use crate::{parsed::Signature, Time};
5+
use crate::{parsed::Signature, Sign, Time};
66
use bstr::{BStr, ByteSlice};
77
use btoi::btoi;
88
use hex::FromHex;
99
use nom::{
10-
bytes::complete::tag,
11-
bytes::complete::{take_while1, take_while_m_n},
12-
character::is_alphabetic,
13-
sequence::{preceded, terminated},
14-
IResult,
10+
bytes::{
11+
complete::{
12+
take_till,
13+
take_until,
14+
take_while,
15+
tag,
16+
take_while1,
17+
take_while_m_n
18+
}
19+
},
20+
branch::alt,
21+
character::{
22+
is_digit,
23+
is_alphabetic
24+
},
25+
sequence::{
26+
delimited,
27+
pair,
28+
tuple,
29+
preceded,
30+
terminated
31+
},
32+
IResult
1533
};
16-
use nom::sequence::{delimited, pair, tuple};
17-
use nom::bytes::complete::{take_until, take_till, take_while};
18-
use nom::character::is_digit;
19-
use nom::branch::alt;
34+
use nom::bytes::complete::take;
2035

2136
#[derive(PartialEq, Eq, Debug, Hash)]
2237
pub struct Tag<'data> {
@@ -93,11 +108,40 @@ fn is_hex_digit_lc(b: u8) -> bool {
93108
const NL: &[u8] = b"\n";
94109
const SPACE: &[u8] = b" ";
95110
pub(crate) fn parse_signature_nom(i: &[u8]) -> IResult<&[u8], Signature, Error> {
96-
let (i, (name, email, time_in_seconds, tzofs)) = tuple((terminated(take_till(|b| b == b'<'), tag(b"<")),
97-
terminated(take_till(|b| b == b'>'), tag(b"> ")),
98-
terminated(take_till(|b| b == SPACE[0]), tag(SPACE)),
99-
pair(alt((tag(b"-"), tag(b"+"))), take_while_m_n(4usize, 4, |b| is_digit(b)))))(i)?;
100-
unimplemented!("parse signature")
111+
let (i, (name, email, time_in_seconds, tzsign, tzhour, tzminute)) = tuple((
112+
terminated(take_until(&b" <"[..]), take(2usize)),
113+
terminated(take_until(&b"> "[..]), take(2usize)),
114+
terminated(take_until(SPACE), take(1usize)),
115+
alt((tag(b"-"), tag(b"+"))),
116+
take_while_m_n(2usize, 2, |b| is_digit(b)),
117+
take_while_m_n(2usize, 2, |b| is_digit(b)),
118+
))(i)
119+
.map_err(Error::context(
120+
"tagger <name> <<email>> <time seconds since epoch> <+|-><HHMM>",
121+
))?;
122+
123+
let sign = if tzsign[0] == b'-' {
124+
Sign::Minus
125+
} else {
126+
Sign::Plus
127+
};
128+
let hours = btoi::<i32>(&tzhour)
129+
.map_err(|e| nom::Err::Error(Error::ParseIntegerError("invalid 'hours' string", tzhour.to_owned(), e)))?;
130+
let minutes = btoi::<i32>(&tzminute)
131+
.map_err(|e| nom::Err::Error(Error::ParseIntegerError("invalid 'minutes' string", tzminute.to_owned(), e)))?;
132+
let offset = (hours * 3600 + minutes * 60);
133+
134+
Ok((i, Signature {
135+
name: name.as_bstr(),
136+
email: email.as_bstr(),
137+
time: Time {
138+
time: btoi::<u32>(time_in_seconds).map_err(|e| {
139+
nom::Err::Error(Error::ParseIntegerError("Could parse to seconds", time_in_seconds.to_owned(), e))
140+
})?,
141+
offset,
142+
sign
143+
}
144+
}))
101145
}
102146

103147
pub(crate) fn parse_tag_nom(i: &[u8]) -> IResult<&[u8], Tag, Error> {
@@ -114,11 +158,15 @@ pub(crate) fn parse_tag_nom(i: &[u8]) -> IResult<&[u8], Tag, Error> {
114158
.map_err(Error::context("type <object kind>"))?;
115159
let kind = crate::Kind::from_bytes(kind)?;
116160

117-
let (i, tag_version) = terminated(preceded(tag(b"tag "), take_while1(|b| b != NL[0])), tag(NL))(i)
118-
.map_err(Error::context("tag <version>"))?;
161+
let (i, tag_version) =
162+
terminated(preceded(tag(b"tag "), take_while1(|b| b != NL[0])), tag(NL))(i)
163+
.map_err(Error::context("tag <version>"))?;
119164

120-
let (i, tagger) = terminated(preceded(tag(b"tagger "), take_while1(|b| b != NL[0])), tag(NL))(i)
121-
.map_err(Error::context("tagger <signature>"))?;
165+
let (i, tagger) = terminated(
166+
preceded(tag(b"tagger "), take_while1(|b| b != NL[0])),
167+
tag(NL),
168+
)(i)
169+
.map_err(Error::context("tagger <signature>"))?;
122170
unimplemented!("parse message nom")
123171
}
124172

git-object/src/parsed/tests/mod.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use super::*;
2-
use hex::FromHex;
3-
use std::path::PathBuf;
42
use crate::parsed::tag::parse_signature_nom;
53
use crate::{parsed, Sign};
64
use bstr::ByteSlice;
5+
use hex::FromHex;
6+
use std::path::PathBuf;
77

88
pub fn bin(hex: &str) -> [u8; 20] {
99
<[u8; 20]>::from_hex(hex).unwrap()
@@ -21,13 +21,18 @@ mod tag;
2121

2222
#[test]
2323
fn parse_signature() {
24-
assert_eq!(parse_signature_nom(b"Sebastian Thiel <byronimo@gmail.com> 1528473343 +0230").unwrap().1, parsed::Signature {
25-
name: b"Sebastian Thiel".as_bstr(),
26-
email: b"byronimo@gmail.com".as_bstr(),
27-
time: Time {
28-
time: 1528473343,
29-
offset: 9000,
30-
sign: Sign::Plus,
31-
},
32-
});
24+
assert_eq!(
25+
parse_signature_nom(b"Sebastian Thiel <byronimo@gmail.com> 1528473343 +0230")
26+
.unwrap()
27+
.1,
28+
parsed::Signature {
29+
name: b"Sebastian Thiel".as_bstr(),
30+
email: b"byronimo@gmail.com".as_bstr(),
31+
time: Time {
32+
time: 1528473343,
33+
offset: 9000,
34+
sign: Sign::Plus,
35+
},
36+
}
37+
);
3338
}

git-object/src/parsed/tests/tag.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@ use pretty_assertions::assert_eq;
88

99
mod nom {
1010
use super::fixture_bytes;
11-
use crate::parsed::tests::tag::tag_fixture;
1211
use crate::parsed::tag::{parse_signature_nom, parse_tag_nom};
12+
use crate::parsed::tests::tag::tag_fixture;
1313

1414
#[test]
1515
fn parse_tag() {
1616
let fixture = fixture_bytes("tag.txt");
1717
assert_eq!(parse_tag_nom(&fixture).unwrap().1, tag_fixture(9000));
1818
}
19-
2019
}
2120

2221
#[test]

0 commit comments

Comments
 (0)