blob: 0b8243ef34b67510e57cca0287887a8ac24fcb25 [file] [log] [blame]
use std::borrow::Cow;
use smallvec::SmallVec;
use winnow::{
combinator::{alt, eof, opt, preceded, repeat, rest, terminated},
error::{AddContext, ParserError, StrContext},
prelude::*,
token::take_till1,
};
use crate::{parse, parse::NL, BStr, ByteSlice, CommitRef};
pub fn message<'a, E: ParserError<&'a [u8]> + AddContext<&'a [u8], StrContext>>(
i: &mut &'a [u8],
) -> PResult<&'a BStr, E> {
if i.is_empty() {
// newline + [message]
return Err(
winnow::error::ErrMode::from_error_kind(i, winnow::error::ErrorKind::Eof)
.add_context(i, StrContext::Expected("newline + <message>".into())),
);
}
preceded(NL, rest.map(ByteSlice::as_bstr))
.context(StrContext::Expected(
"a newline separates headers from the message".into(),
))
.parse_next(i)
}
pub fn commit<'a, E: ParserError<&'a [u8]> + AddContext<&'a [u8], StrContext>>(
i: &mut &'a [u8],
) -> PResult<CommitRef<'a>, E> {
(
(|i: &mut _| parse::header_field(i, b"tree", parse::hex_hash))
.context(StrContext::Expected("tree <40 lowercase hex char>".into())),
repeat(0.., |i: &mut _| parse::header_field(i, b"parent", parse::hex_hash))
.map(|p: Vec<_>| p)
.context(StrContext::Expected(
"zero or more 'parent <40 lowercase hex char>'".into(),
)),
(|i: &mut _| parse::header_field(i, b"author", parse::signature))
.context(StrContext::Expected("author <signature>".into())),
(|i: &mut _| parse::header_field(i, b"committer", parse::signature))
.context(StrContext::Expected("committer <signature>".into())),
opt(|i: &mut _| parse::header_field(i, b"encoding", take_till1(NL)))
.context(StrContext::Expected("encoding <encoding>".into())),
repeat(
0..,
alt((
parse::any_header_field_multi_line.map(|(k, o)| (k.as_bstr(), Cow::Owned(o))),
|i: &mut _| {
parse::any_header_field(i, take_till1(NL)).map(|(k, o)| (k.as_bstr(), Cow::Borrowed(o.as_bstr())))
},
)),
)
.context(StrContext::Expected("<field> <single-line|multi-line>".into())),
terminated(message, eof),
)
.map(
|(tree, parents, author, committer, encoding, extra_headers, message)| CommitRef {
tree,
parents: SmallVec::from(parents),
author,
committer,
encoding: encoding.map(ByteSlice::as_bstr),
message,
extra_headers,
},
)
.parse_next(i)
}