|
| 1 | +use winnow::combinator::eof; |
| 2 | +use winnow::combinator::terminated; |
| 3 | +use winnow::error::ErrMode; |
| 4 | +use winnow::error::ErrorKind; |
| 5 | +use winnow::error::ParserError; |
| 6 | +use winnow::stream::Offset; |
| 7 | +use winnow::stream::Stream; |
| 8 | +use winnow::stream::StreamIsPartial; |
| 9 | +use winnow::Parser; |
| 10 | + |
| 11 | +/// Call the `repeat` parser until the `end` parser produces a result. |
| 12 | +/// |
| 13 | +/// Then, return the input consumed until the `end` parser was called, and the result of the `end` |
| 14 | +/// parser. |
| 15 | +/// |
| 16 | +/// See: <https://github.com/winnow-rs/winnow/pull/541> |
| 17 | +pub fn recognize_till<I, Discard, O, E>( |
| 18 | + mut repeat: impl Parser<I, Discard, E>, |
| 19 | + mut end: impl Parser<I, O, E>, |
| 20 | +) -> impl Parser<I, (<I as Stream>::Slice, O), E> |
| 21 | +where |
| 22 | + I: Stream, |
| 23 | + E: ParserError<I>, |
| 24 | +{ |
| 25 | + move |input: &mut I| { |
| 26 | + let start = input.checkpoint(); |
| 27 | + |
| 28 | + loop { |
| 29 | + let before_end = input.checkpoint(); |
| 30 | + match end.parse_next(input) { |
| 31 | + Ok(end_parsed) => { |
| 32 | + let after_end = input.checkpoint(); |
| 33 | + |
| 34 | + let offset_to_before_end = before_end.offset_from(&start); |
| 35 | + input.reset(start); |
| 36 | + let input_until_end = input.next_slice(offset_to_before_end); |
| 37 | + input.reset(after_end); |
| 38 | + |
| 39 | + return Ok((input_until_end, end_parsed)); |
| 40 | + } |
| 41 | + Err(ErrMode::Backtrack(_)) => { |
| 42 | + input.reset(before_end); |
| 43 | + match repeat.parse_next(input) { |
| 44 | + Ok(_) => {} |
| 45 | + Err(e) => return Err(e.append(input, ErrorKind::Many)), |
| 46 | + } |
| 47 | + } |
| 48 | + Err(e) => return Err(e), |
| 49 | + } |
| 50 | + } |
| 51 | + } |
| 52 | +} |
| 53 | + |
| 54 | +/// Like [`recognize_till`], but it also applies a `transform` parser to the recognized input. |
| 55 | +pub fn transform_till<I, O1, O2, Discard, E>( |
| 56 | + mut repeat: impl Parser<I, Discard, E>, |
| 57 | + mut transform: impl Parser<<I as Stream>::Slice, O1, E>, |
| 58 | + mut end: impl Parser<I, O2, E>, |
| 59 | +) -> impl Parser<I, (O1, O2), E> |
| 60 | +where |
| 61 | + I: Stream, |
| 62 | + E: ParserError<I>, |
| 63 | + E: ParserError<<I as Stream>::Slice>, |
| 64 | + <I as Stream>::Slice: Stream + StreamIsPartial, |
| 65 | +{ |
| 66 | + move |input: &mut I| { |
| 67 | + let (mut until_end, end_parsed) = |
| 68 | + recognize_till(repeat.by_ref(), end.by_ref()).parse_next(input)?; |
| 69 | + |
| 70 | + let inner_parsed = terminated(transform.by_ref(), eof) |
| 71 | + .parse_next(&mut until_end) |
| 72 | + .map_err(|err_mode| match err_mode { |
| 73 | + ErrMode::Incomplete(_) => { |
| 74 | + panic!("complete parsers should not report `ErrMode::Incomplete(_)`") |
| 75 | + } |
| 76 | + ErrMode::Backtrack(inner) | ErrMode::Cut(inner) => ErrMode::Cut(inner), |
| 77 | + })?; |
| 78 | + |
| 79 | + Ok((inner_parsed, end_parsed)) |
| 80 | + } |
| 81 | +} |
0 commit comments