git_object/tag/
decode.rs

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            // Empty message. That's OK.
57            return Ok((&[], (&[], &[])));
58        }
59        // an empty signature message signals that there is none - the function signature is needed
60        // to work with 'alt(…)'. PGP signatures are never empty
61        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}