summaryrefslogtreecommitdiffstats
path: root/vendor/gix-object/src/commit/message/decode.rs
blob: 20c171c8751c7cbbd638a0f7e357cd8a89198667 (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
use winnow::{
    combinator::{alt, eof, preceded, rest, terminated},
    error::ParserError,
    prelude::*,
    stream::{Offset, Stream},
    token::take_till,
};

use crate::bstr::{BStr, ByteSlice};

pub(crate) fn newline<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<&'a [u8], E> {
    alt((b"\n", b"\r\n")).parse_next(i)
}

fn subject_and_body<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<(&'a BStr, Option<&'a BStr>), E> {
    let start_i = *i;
    let start = i.checkpoint();
    while !i.is_empty() {
        match take_till::<_, _, E>(1.., |c| c == b'\n' || c == b'\r').parse_next(i) {
            Ok(_) => {
                let consumed_bytes = i.offset_from(&start);
                match preceded((newline::<E>, newline::<E>), rest).parse_next(i) {
                    Ok(body) => {
                        let body = (!body.is_empty()).then(|| body.as_bstr());
                        return Ok((start_i[0usize..consumed_bytes].as_bstr(), body));
                    }
                    Err(_) => match i.next_token() {
                        Some(_) => {}
                        None => break,
                    },
                }
            }
            Err(_) => match i.next_token() {
                Some(_) => {}
                None => break,
            },
        }
    }

    i.reset(start);
    rest.map(|r: &[u8]| (r.as_bstr(), None)).parse_next(i)
}

/// Returns title and body, without separator
pub fn message(mut input: &[u8]) -> (&BStr, Option<&BStr>) {
    terminated(subject_and_body::<()>, eof)
        .parse_next(&mut input)
        .expect("cannot fail")
}