git_object/commit/message/
decode.rs

1use nom::{
2    branch::alt,
3    bytes::complete::{tag, take_till1},
4    combinator::all_consuming,
5    error::ParseError,
6    sequence::pair,
7    IResult,
8};
9
10use crate::bstr::{BStr, ByteSlice};
11
12pub(crate) fn newline<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E> {
13    alt((tag(b"\r\n"), tag(b"\n")))(i)
14}
15
16fn subject_and_body<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], (&'a BStr, Option<&'a BStr>), E> {
17    let mut c = i;
18    let mut consumed_bytes = 0;
19    while !c.is_empty() {
20        c = match take_till1::<_, _, E>(|c| c == b'\n' || c == b'\r')(c) {
21            Ok((i1, segment)) => {
22                consumed_bytes += segment.len();
23                match pair::<_, _, _, E, _, _>(newline, newline)(i1) {
24                    Ok((body, _)) => {
25                        return Ok((
26                            &[],
27                            (
28                                i[0usize..consumed_bytes].as_bstr(),
29                                (!body.is_empty()).then(|| body.as_bstr()),
30                            ),
31                        ));
32                    }
33                    Err(_) => match i1.get(1..) {
34                        Some(next) => {
35                            consumed_bytes += 1;
36                            next
37                        }
38                        None => break,
39                    },
40                }
41            }
42            Err(_) => match c.get(1..) {
43                Some(next) => {
44                    consumed_bytes += 1;
45                    next
46                }
47                None => break,
48            },
49        };
50    }
51    Ok((&[], (i.as_bstr(), None)))
52}
53
54/// Returns title and body, without separator
55pub fn message(input: &[u8]) -> (&BStr, Option<&BStr>) {
56    all_consuming(subject_and_body::<()>)(input).expect("cannot fail").1
57}