gix-object 0.58.0

Immutable and mutable git objects with decoding and encoding support
Documentation
use winnow::{
    combinator::{alt, eof, preceded, terminated},
    error::ParserError,
    prelude::*,
    stream::{Offset, Stream},
    token::{rest, take_till},
};

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

pub(crate) fn newline<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> ModalResult<&'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]) -> ModalResult<(&'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")
}