use crate::{
cache::ParsingCache,
parser::{Parsable, Parser, Source},
result::{Error, ParseResult},
utf8util::read_utf8_char,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Until<E> {
end: E,
min_length: usize,
max_length: Option<usize>,
}
impl<E> Until<E> {
pub fn new(end: E) -> Self {
Self {
end,
min_length: 0,
max_length: None,
}
}
pub fn with_min(end: E, min: usize) -> Self {
Self {
end,
min_length: min,
max_length: None,
}
}
pub fn with_max(end: E, max: usize) -> Self {
Self {
end,
min_length: 0,
max_length: Some(max),
}
}
pub fn with_bounds(end: E, min: usize, max: usize) -> Self {
Self {
end,
min_length: min,
max_length: Some(max),
}
}
}
impl<E, Ctx> Parser<Ctx> for Until<E>
where
E: Parser<Ctx>,
{
type Output = Vec<u8>;
fn id(&self) -> u64 {
use std::any::TypeId;
use std::hash::{DefaultHasher, Hash, Hasher};
let mut hasher = DefaultHasher::new();
TypeId::of::<Self>().hash(&mut hasher);
self.end.id().hash(&mut hasher);
self.min_length.hash(&mut hasher);
self.max_length.hash(&mut hasher);
hasher.finish()
}
fn read<S>(
&self,
source: &mut Source<S>,
cache: &mut impl ParsingCache,
context: &mut Ctx,
) -> ParseResult<Self::Output>
where
S: Parsable,
{
let mut result = Vec::new();
loop {
if let Some(max) = self.max_length {
if result.len() >= max {
break;
}
}
source.push();
match self.end.parse(source, cache, context) {
Ok(_) => {
source.pop(); break;
}
Err(Error::NoMatch) => {
source.pop(); }
Err(err) => {
source.pop();
return Err(err);
}
}
match source.peek1() {
Ok(byte) => {
result.push(byte);
source.advance(1);
}
Err(Error::NoMatch) => {
break;
}
Err(err) => {
return Err(err);
}
}
}
if result.len() < self.min_length {
return Err(Error::NoMatch);
}
Ok(result)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Utf8Until<E> {
end: E,
min_chars: usize,
max_chars: Option<usize>,
}
impl<E> Utf8Until<E> {
pub fn new(end: E) -> Self {
Self {
end,
min_chars: 0,
max_chars: None,
}
}
pub fn with_min(end: E, min: usize) -> Self {
Self {
end,
min_chars: min,
max_chars: None,
}
}
pub fn with_max(end: E, max: usize) -> Self {
Self {
end,
min_chars: 0,
max_chars: Some(max),
}
}
pub fn with_bounds(end: E, min: usize, max: usize) -> Self {
Self {
end,
min_chars: min,
max_chars: Some(max),
}
}
}
impl<E, Ctx> Parser<Ctx> for Utf8Until<E>
where
E: Parser<Ctx>,
{
type Output = String;
fn id(&self) -> u64 {
use std::any::TypeId;
use std::hash::{DefaultHasher, Hash, Hasher};
let mut hasher = DefaultHasher::new();
TypeId::of::<Self>().hash(&mut hasher);
self.end.id().hash(&mut hasher);
self.min_chars.hash(&mut hasher);
self.max_chars.hash(&mut hasher);
hasher.finish()
}
fn read<S>(
&self,
source: &mut Source<S>,
cache: &mut impl ParsingCache,
context: &mut Ctx,
) -> ParseResult<Self::Output>
where
S: Parsable,
{
let mut result = String::new();
let mut char_count = 0;
loop {
if let Some(max) = self.max_chars {
if char_count >= max {
break;
}
}
source.push();
match self.end.parse(source, cache, context) {
Ok(_) => {
source.pop(); break;
}
Err(Error::NoMatch) => {
source.pop(); }
Err(err) => {
source.pop();
return Err(err);
}
}
match read_utf8_char(source) {
Ok(ch) => {
result.push(ch);
char_count += 1;
}
Err(Error::NoMatch) => {
break;
}
Err(err) => {
return Err(err);
}
}
}
if char_count < self.min_chars {
return Err(Error::NoMatch);
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{literal::Literal, parser::parse};
use std::io::Cursor;
#[test]
fn test_id_implementation_different_until_parsers() {
let until1 = Until::new(Literal::from_str("end"));
let until2 = Until::new(Literal::from_str("stop"));
assert_ne!(
<Until<_> as crate::parser::Parser<()>>::id(&until1),
<Until<_> as crate::parser::Parser<()>>::id(&until2),
"Different Until instances should have different IDs to avoid cache collisions"
);
}
#[test]
fn test_id_implementation_same_until_parsers() {
let until1 = Until::new(Literal::from_str("end"));
let until2 = Until::new(Literal::from_str("end"));
assert_eq!(
<Until<_> as crate::parser::Parser<()>>::id(&until1),
<Until<_> as crate::parser::Parser<()>>::id(&until2),
"Identical Until instances should have the same ID for cache efficiency"
);
}
#[test]
fn test_id_implementation_until_different_bounds() {
let until1 = Until::with_min(Literal::from_str("end"), 1);
let until2 = Until::with_min(Literal::from_str("end"), 2);
assert_ne!(
<Until<_> as crate::parser::Parser<()>>::id(&until1),
<Until<_> as crate::parser::Parser<()>>::id(&until2),
"Until instances with different bounds should have different IDs"
);
}
#[test]
fn test_id_implementation_utf8_until_parsers() {
let utf8_until1 = Utf8Until::new(Literal::from_str("end"));
let utf8_until2 = Utf8Until::new(Literal::from_str("stop"));
assert_ne!(
<Utf8Until<_> as crate::parser::Parser<()>>::id(&utf8_until1),
<Utf8Until<_> as crate::parser::Parser<()>>::id(&utf8_until2),
"Different Utf8Until instances should have different IDs to avoid cache collisions"
);
}
#[test]
fn test_until_basic() {
let parser = Until::new(Literal::from_str("end"));
let mut input = Cursor::new(b"hello world end more text");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"hello world ".to_vec());
let end_parser = Literal::from_str("end");
let end_result = parse(end_parser, &mut source).unwrap();
assert_eq!(end_result, b"end".as_slice().into());
}
#[test]
fn test_until_single_character_end() {
let parser = Until::new(Literal::from_str("."));
let mut input = Cursor::new(b"hello.world");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"hello".to_vec());
let dot_parser = Literal::from_str(".");
let dot_result = parse(dot_parser, &mut source).unwrap();
assert_eq!(dot_result, b".".as_slice().into());
}
#[test]
fn test_until_with_min() {
let parser = Until::with_min(Literal::from_str("."), 5);
let mut input1 = Cursor::new(b"hello.world");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(parser, &mut source1).unwrap();
assert_eq!(result1, b"hello".to_vec());
let parser2 = Until::with_min(Literal::from_str("."), 5);
let mut input2 = Cursor::new(b"hi.there");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(parser2, &mut source2);
assert!(result2.is_err());
}
#[test]
fn test_until_with_max() {
let parser = Until::with_max(Literal::from_str("."), 5);
let mut input = Cursor::new(b"hello world.more");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"hello".to_vec());
let remaining = source.peek(11).unwrap();
assert_eq!(remaining, b" world.more");
}
#[test]
fn test_until_with_bounds() {
let parser = Until::with_bounds(Literal::from_str("."), 3, 7);
let mut input = Cursor::new(b"hello world.more");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"hello w".to_vec()); }
#[test]
fn test_until_no_end_found() {
let parser = Until::new(Literal::from_str("."));
let mut input = Cursor::new(b"hello world");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"hello world".to_vec()); }
#[test]
fn test_until_immediate_end() {
let parser = Until::new(Literal::from_str("hello"));
let mut input = Cursor::new(b"hello world");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"".to_vec());
let hello_parser = Literal::from_str("hello");
let hello_result = parse(hello_parser, &mut source).unwrap();
assert_eq!(hello_result, b"hello".as_slice().into());
}
#[test]
fn test_until_empty_input() {
let parser = Until::new(Literal::from_str("."));
let mut input = Cursor::new(b"");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"".to_vec()); }
#[test]
fn test_until_multichar_delimiter() {
let parser = Until::new(Literal::from_str("end"));
let mut input = Cursor::new(b"start middle end final");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"start middle ".to_vec());
let end_parser = Literal::from_str("end");
let end_result = parse(end_parser, &mut source).unwrap();
assert_eq!(end_result, b"end".as_slice().into());
let final_parser = Literal::from_str(" final");
let final_result = parse(final_parser, &mut source).unwrap();
assert_eq!(final_result, b" final".as_slice().into());
}
#[test]
fn test_until_in_sequence() {
use crate::seq;
let parser = seq![
Literal::from_str("start"),
Literal::from_str("123"),
Literal::from_str("_"),
Until::new(Literal::from_str("end")),
Literal::from_str("end")
];
let mut input = Cursor::new(b"start123_abcend");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"start".as_slice().into());
assert_eq!(result.1.0, b"123".as_slice().into());
assert_eq!(result.1.1.0, b"_".as_slice().into());
assert_eq!(result.1.1.1.0, b"abc".to_vec());
assert_eq!(result.1.1.1.1.0, b"end".as_slice().into());
}
#[test]
fn test_utf8_until_basic() {
let parser = Utf8Until::new(Literal::from_str("end"));
let mut input = Cursor::new("hello 世界 end more".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, "hello 世界 ");
let end_parser = Literal::from_str("end");
let end_result = parse(end_parser, &mut source).unwrap();
assert_eq!(end_result, b"end".as_slice().into());
}
#[test]
fn test_utf8_until_character_boundaries() {
let parser = Utf8Until::new(Literal::from_str("."));
let mut input = Cursor::new("café 世界.more".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, "café 世界");
let dot_parser = Literal::from_str(".");
let dot_result = parse(dot_parser, &mut source).unwrap();
assert_eq!(dot_result, b".".as_slice().into());
}
#[test]
fn test_utf8_until_with_max_chars() {
let parser = Utf8Until::with_max(Literal::from_str("."), 3);
let mut input = Cursor::new("café 世界 more.end".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, "caf");
}
#[test]
fn test_utf8_until_with_min_chars() {
let parser = Utf8Until::with_min(Literal::from_str("."), 3);
let mut input1 = Cursor::new("世界测.test".as_bytes());
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(parser, &mut source1).unwrap();
assert_eq!(result1, "世界测");
let parser2 = Utf8Until::with_min(Literal::from_str("."), 3);
let mut input2 = Cursor::new("世界.test".as_bytes());
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(parser2, &mut source2);
assert!(result2.is_err());
}
#[test]
fn test_utf8_until_invalid_utf8() {
let parser = Utf8Until::with_min(Literal::from_str("end"), 1);
let mut input = Cursor::new(b"\xFF\xFEend");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source);
assert!(result.is_err());
}
#[test]
fn test_utf8_until_emoji() {
let parser = Utf8Until::new(Literal::from_str("!"));
let mut input = Cursor::new("Hello 👋 World 🌍!".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, "Hello 👋 World 🌍");
let bang_parser = Literal::from_str("!");
let bang_result = parse(bang_parser, &mut source).unwrap();
assert_eq!(bang_result, b"!".as_slice().into());
}
#[test]
fn test_utf8_until_bounds() {
let parser = Utf8Until::with_bounds(Literal::from_str("."), 2, 4);
let mut input = Cursor::new("世界测试.more".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, "世界测试"); }
#[test]
fn test_until_complex_end_parser() {
use crate::sequence::Sequence;
let end_parser = Sequence::new(Literal::from_str("end"), Literal::from_str("_tag"));
let parser = Until::new(end_parser);
let mut input = Cursor::new(b"some content before end_tag and after");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"some content before ".to_vec());
let end_parser2 = Sequence::new(Literal::from_str("end"), Literal::from_str("_tag"));
let end_result = parse(end_parser2, &mut source).unwrap();
assert_eq!(end_result.0, b"end".as_slice().into());
assert_eq!(end_result.1, b"_tag".as_slice().into());
}
#[test]
fn test_until_with_repeat_end_parser() {
use crate::repeat::Repeat;
let end_parser = Repeat::with_min(Literal::from_str("x"), 2);
let parser = Until::new(end_parser);
let mut input = Cursor::new(b"content before xxxx and after");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"content before ".to_vec());
}
#[test]
fn test_until_multiple_potential_ends() {
let parser = Until::new(Literal::from_str("end"));
let mut input = Cursor::new(b"start end middle end final");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"start ".to_vec());
let remaining = source.peek(20).unwrap();
assert_eq!(remaining, b"end middle end final");
}
#[test]
fn test_until_overlapping_end_patterns() {
let parser = Until::new(Literal::from_str("aba"));
let mut input = Cursor::new(b"xyzababadef");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"xyz".to_vec());
let remaining = source.peek(8).unwrap();
assert_eq!(remaining, b"ababadef");
}
#[test]
fn test_until_boundary_edge_cases() {
let parser = Until::with_min(Literal::from_str("."), 5);
let mut input1 = Cursor::new(b"12345.rest");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(parser, &mut source1).unwrap();
assert_eq!(result1, b"12345".to_vec());
let parser2 = Until::with_min(Literal::from_str("."), 5);
let mut input2 = Cursor::new(b"1234.rest");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(parser2, &mut source2);
assert!(result2.is_err());
let parser3 = Until::with_max(Literal::from_str("."), 3);
let mut input3 = Cursor::new(b"12345678.rest");
let mut source3 = crate::parser::Source::new(&mut input3);
let result3 = parse(parser3, &mut source3).unwrap();
assert_eq!(result3, b"123".to_vec()); }
#[test]
fn test_until_very_large_content() {
let large_content = b"x".repeat(10000);
let mut input_bytes = Vec::new();
input_bytes.extend_from_slice(&large_content);
input_bytes.extend_from_slice(b"END");
let parser = Until::new(Literal::from_str("END"));
let mut input = Cursor::new(&input_bytes);
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.len(), 10000);
assert!(result.iter().all(|&b| b == b'x'));
}
#[test]
fn test_utf8_until_complex_end_condition() {
use crate::utf8class::Utf8Class;
let end_parser = Utf8Class::unicode_digits();
let parser = Utf8Until::new(end_parser);
let mut input = Cursor::new("Hello 世界 ১২৩ more text".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, "Hello 世界 ");
}
#[test]
fn test_utf8_until_mixed_content() {
let parser = Utf8Until::new(Literal::from_str("🔚"));
let mut input = Cursor::new("ASCII café 世界 привет 🔚 end".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, "ASCII café 世界 привет ");
let remaining_bytes = source.peek(8).unwrap();
assert_eq!(remaining_bytes, "🔚 end".as_bytes());
}
#[test]
fn test_until_position_after_parsing() {
let parser = Until::new(Literal::from_str("||"));
let mut input = Cursor::new(b"item1|item2||item3|item4");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"item1|item2".to_vec());
let delimiter = source.peek(2).unwrap();
assert_eq!(delimiter, b"||");
source.advance(2);
let remaining = source.peek(5).unwrap();
assert_eq!(remaining, b"item3");
}
#[test]
fn test_until_with_empty_end_condition() {
use crate::literal::Literal;
let empty_literal = Literal::from_str("");
let parser = Until::new(empty_literal);
let mut input = Cursor::new(b"some content");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, Vec::<u8>::new());
}
#[test]
fn test_utf8_until_character_counting_accuracy() {
let parser = Utf8Until::with_max(Literal::from_str("!"), 3);
let mut input = Cursor::new("🇺🇸é!more".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
println!("Result: '{}', len: {}", result, result.chars().count());
}
}