git_object/commit/
decode.rs

1use std::borrow::Cow;
2
3use nom::{
4    branch::alt,
5    bytes::complete::{is_not, tag},
6    combinator::{all_consuming, opt},
7    error::{context, ContextError, ParseError},
8    multi::many0,
9    IResult, Parser,
10};
11use smallvec::SmallVec;
12
13use crate::{parse, parse::NL, BStr, ByteSlice, CommitRef};
14
15pub fn message<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], &'a BStr, E> {
16    if i.is_empty() {
17        // newline + [message]
18        return Err(nom::Err::Error(E::add_context(
19            i,
20            "newline + <message>",
21            E::from_error_kind(i, nom::error::ErrorKind::Eof),
22        )));
23    }
24    let (i, _) = context("a newline separates headers from the message", tag(NL))(i)?;
25    Ok((&[], i.as_bstr()))
26}
27
28pub fn commit<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
29    i: &'a [u8],
30) -> IResult<&'a [u8], CommitRef<'_>, E> {
31    let (i, tree) = context("tree <40 lowercase hex char>", |i| {
32        parse::header_field(i, b"tree", parse::hex_hash)
33    })(i)?;
34    let (i, parents) = context(
35        "zero or more 'parent <40 lowercase hex char>'",
36        many0(|i| parse::header_field(i, b"parent", parse::hex_hash)),
37    )(i)?;
38    let (i, author) = context("author <signature>", |i| {
39        parse::header_field(i, b"author", parse::signature)
40    })(i)?;
41    let (i, committer) = context("committer <signature>", |i| {
42        parse::header_field(i, b"committer", parse::signature)
43    })(i)?;
44    let (i, encoding) = context(
45        "encoding <encoding>",
46        opt(|i| parse::header_field(i, b"encoding", is_not(NL))),
47    )(i)?;
48    let (i, extra_headers) = context(
49        "<field> <single-line|multi-line>",
50        many0(alt((
51            parse::any_header_field_multi_line.map(|(k, o)| (k.as_bstr(), Cow::Owned(o))),
52            |i| {
53                parse::any_header_field(i, is_not(NL)).map(|(i, (k, o))| (i, (k.as_bstr(), Cow::Borrowed(o.as_bstr()))))
54            },
55        ))),
56    )(i)?;
57    let (i, message) = all_consuming(message)(i)?;
58
59    Ok((
60        i,
61        CommitRef {
62            tree,
63            parents: SmallVec::from(parents),
64            author,
65            committer,
66            encoding: encoding.map(ByteSlice::as_bstr),
67            message,
68            extra_headers,
69        },
70    ))
71}