]>
Commit | Line | Data |
---|---|---|
781aab86 FG |
1 | use winnow::{ |
2 | combinator::{alt, eof, preceded, rest, terminated}, | |
3 | error::ParserError, | |
4 | prelude::*, | |
5 | stream::{Offset, Stream}, | |
4b012472 | 6 | token::take_till, |
0a29b90c FG |
7 | }; |
8 | ||
9 | use crate::bstr::{BStr, ByteSlice}; | |
10 | ||
781aab86 FG |
11 | pub(crate) fn newline<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<&'a [u8], E> { |
12 | alt((b"\n", b"\r\n")).parse_next(i) | |
0a29b90c FG |
13 | } |
14 | ||
781aab86 FG |
15 | fn subject_and_body<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<(&'a BStr, Option<&'a BStr>), E> { |
16 | let start_i = *i; | |
17 | let start = i.checkpoint(); | |
18 | while !i.is_empty() { | |
4b012472 | 19 | match take_till::<_, _, E>(1.., |c| c == b'\n' || c == b'\r').parse_next(i) { |
781aab86 FG |
20 | Ok(_) => { |
21 | let consumed_bytes = i.offset_from(&start); | |
22 | match preceded((newline::<E>, newline::<E>), rest).parse_next(i) { | |
23 | Ok(body) => { | |
24 | let body = (!body.is_empty()).then(|| body.as_bstr()); | |
25 | return Ok((start_i[0usize..consumed_bytes].as_bstr(), body)); | |
0a29b90c | 26 | } |
781aab86 FG |
27 | Err(_) => match i.next_token() { |
28 | Some(_) => {} | |
0a29b90c FG |
29 | None => break, |
30 | }, | |
31 | } | |
32 | } | |
781aab86 FG |
33 | Err(_) => match i.next_token() { |
34 | Some(_) => {} | |
0a29b90c FG |
35 | None => break, |
36 | }, | |
781aab86 | 37 | } |
0a29b90c | 38 | } |
781aab86 FG |
39 | |
40 | i.reset(start); | |
41 | rest.map(|r: &[u8]| (r.as_bstr(), None)).parse_next(i) | |
0a29b90c FG |
42 | } |
43 | ||
44 | /// Returns title and body, without separator | |
781aab86 FG |
45 | pub fn message(mut input: &[u8]) -> (&BStr, Option<&BStr>) { |
46 | terminated(subject_and_body::<()>, eof) | |
47 | .parse_next(&mut input) | |
48 | .expect("cannot fail") | |
0a29b90c | 49 | } |