use winnow::prelude::*;
#[inline]
fn skip_whitespace(input: &mut &str) {
let bytes = input.as_bytes();
let len = super::simd::scan_whitespace(bytes);
*input = &input[len..];
}
#[inline]
pub fn ws<'a, F, O>(mut parser: F) -> impl Parser<&'a str, O, winnow::error::ContextError>
where
F: Parser<&'a str, O, winnow::error::ContextError>,
{
move |input: &mut &'a str| {
skip_whitespace(input);
let output = parser.parse_next(input)?;
skip_whitespace(input);
Ok(output)
}
}
#[inline]
#[must_use]
pub fn tag_no_case<'a>(
tag: &'static str,
) -> impl Parser<&'a str, &'a str, winnow::error::ContextError> {
move |input: &mut &'a str| {
let tag_len = tag.len();
if input.len() < tag_len {
return super::backtrack();
}
let input_start = &input[..tag_len];
if input_start.eq_ignore_ascii_case(tag) {
let result = input_start;
*input = &input[tag_len..];
Ok(result)
} else {
super::backtrack()
}
}
}
#[inline]
#[must_use]
pub fn balanced_delimited<'a>(
open: char,
close: char,
) -> impl Parser<&'a str, &'a str, winnow::error::ContextError> {
move |input: &mut &'a str| {
if !input.starts_with(open) {
return super::backtrack();
}
let mut depth = 0;
let mut pos = 0;
let bytes = input.as_bytes();
let mut i = 0;
while i < bytes.len() {
let byte = bytes[i];
if byte == b'\\' && i + 1 < bytes.len() {
i += 2;
continue;
} else if byte == open as u8 {
depth += 1;
} else if byte == close as u8 {
depth -= 1;
if depth == 0 {
pos = i + 1;
break;
}
}
i += 1;
}
if depth == 0 {
let result = &input[1..pos - 1];
*input = &input[pos..];
Ok(result)
} else {
super::backtrack()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ws() {
let mut input = " hello world ";
let mut parser = ws("hello");
let result = parser.parse_next(&mut input).unwrap();
assert_eq!(result, "hello");
assert_eq!(input, "world ");
}
#[test]
fn test_tag_no_case() {
let mut input = "ARTICLE{...}";
let result = tag_no_case("article").parse_next(&mut input).unwrap();
assert_eq!(result, "ARTICLE");
assert_eq!(input, "{...}");
let mut input = "Article{...}";
let result = tag_no_case("article").parse_next(&mut input).unwrap();
assert_eq!(result, "Article");
assert_eq!(input, "{...}");
}
}