1use nom::{
2 branch::alt,
3 bytes::complete::{tag, take_until, take_while, take_while1},
4 character::is_alphabetic,
5 combinator::{all_consuming, opt, recognize},
6 error::{context, ContextError, ParseError},
7 sequence::{preceded, tuple},
8 IResult,
9};
10
11use crate::{parse, parse::NL, BStr, ByteSlice, TagRef};
12
13pub fn git_tag<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(i: &'a [u8]) -> IResult<&[u8], TagRef<'a>, E> {
14 let (i, target) = context("object <40 lowercase hex char>", |i| {
15 parse::header_field(i, b"object", parse::hex_hash)
16 })(i)?;
17
18 let (i, kind) = context("type <object kind>", |i| {
19 parse::header_field(i, b"type", take_while1(is_alphabetic))
20 })(i)?;
21 let kind = crate::Kind::from_bytes(kind)
22 .map_err(|_| nom::Err::Error(E::from_error_kind(i, nom::error::ErrorKind::MapRes)))?;
23
24 let (i, tag_version) = context("tag <version>", |i| {
25 parse::header_field(i, b"tag", take_while1(|b| b != NL[0]))
26 })(i)?;
27
28 let (i, signature) = context(
29 "tagger <signature>",
30 opt(|i| parse::header_field(i, b"tagger", parse::signature)),
31 )(i)?;
32 let (i, (message, pgp_signature)) = all_consuming(message)(i)?;
33 Ok((
34 i,
35 TagRef {
36 target,
37 name: tag_version.as_bstr(),
38 target_kind: kind,
39 message,
40 tagger: signature,
41 pgp_signature,
42 },
43 ))
44}
45
46pub fn message<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], (&'a BStr, Option<&'a BStr>), E> {
47 const PGP_SIGNATURE_BEGIN: &[u8] = b"\n-----BEGIN PGP SIGNATURE-----";
48 const PGP_SIGNATURE_END: &[u8] = b"-----END PGP SIGNATURE-----";
49
50 if i.is_empty() {
51 return Ok((i, (i.as_bstr(), None)));
52 }
53 let (i, _) = tag(NL)(i)?;
54 fn all_to_end<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], (&'a [u8], &'a [u8]), E> {
55 if i.is_empty() {
56 return Ok((&[], (&[], &[])));
58 }
59 Ok((&[], (i, &[])))
62 }
63 let (i, (message, signature)) = alt((
64 tuple((
65 take_until(PGP_SIGNATURE_BEGIN),
66 preceded(
67 tag(NL),
68 recognize(tuple((
69 tag(&PGP_SIGNATURE_BEGIN[1..]),
70 take_until(PGP_SIGNATURE_END),
71 tag(PGP_SIGNATURE_END),
72 take_while(|_| true),
73 ))),
74 ),
75 )),
76 all_to_end,
77 ))(i)?;
78 let (i, _) = opt(tag(NL))(i)?;
79 Ok((
80 i,
81 (
82 message.as_bstr(),
83 if signature.is_empty() {
84 None
85 } else {
86 Some(signature.as_bstr())
87 },
88 ),
89 ))
90}