summaryrefslogtreecommitdiffstats
path: root/vendor/gix-object/src/commit/decode.rs
blob: 821feaabba5d1c439d4036237afdefe3e93a0689 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
use std::borrow::Cow;

use nom::{
    branch::alt,
    bytes::complete::{is_not, tag},
    combinator::{all_consuming, opt},
    error::{context, ContextError, ParseError},
    multi::many0,
    IResult, Parser,
};
use smallvec::SmallVec;

use crate::{parse, parse::NL, BStr, ByteSlice, CommitRef};

pub fn message<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], &'a BStr, E> {
    if i.is_empty() {
        // newline + [message]
        return Err(nom::Err::Error(E::add_context(
            i,
            "newline + <message>",
            E::from_error_kind(i, nom::error::ErrorKind::Eof),
        )));
    }
    let (i, _) = context("a newline separates headers from the message", tag(NL))(i)?;
    Ok((&[], i.as_bstr()))
}

pub fn commit<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
    i: &'a [u8],
) -> IResult<&'a [u8], CommitRef<'_>, E> {
    let (i, tree) = context("tree <40 lowercase hex char>", |i| {
        parse::header_field(i, b"tree", parse::hex_hash)
    })(i)?;
    let (i, parents) = context(
        "zero or more 'parent <40 lowercase hex char>'",
        many0(|i| parse::header_field(i, b"parent", parse::hex_hash)),
    )(i)?;
    let (i, author) = context("author <signature>", |i| {
        parse::header_field(i, b"author", parse::signature)
    })(i)?;
    let (i, committer) = context("committer <signature>", |i| {
        parse::header_field(i, b"committer", parse::signature)
    })(i)?;
    let (i, encoding) = context(
        "encoding <encoding>",
        opt(|i| parse::header_field(i, b"encoding", is_not(NL))),
    )(i)?;
    let (i, extra_headers) = context(
        "<field> <single-line|multi-line>",
        many0(alt((
            parse::any_header_field_multi_line.map(|(k, o)| (k.as_bstr(), Cow::Owned(o))),
            |i| {
                parse::any_header_field(i, is_not(NL)).map(|(i, (k, o))| (i, (k.as_bstr(), Cow::Borrowed(o.as_bstr()))))
            },
        ))),
    )(i)?;
    let (i, message) = all_consuming(message)(i)?;

    Ok((
        i,
        CommitRef {
            tree,
            parents: SmallVec::from(parents),
            author,
            committer,
            encoding: encoding.map(ByteSlice::as_bstr),
            message,
            extra_headers,
        },
    ))
}