use crate::{
cache::ParsingCache,
parser::{Parsable, Parser, Source},
result::{Error, ParseResult},
};
#[derive(Clone)]
pub struct Class<F = fn(u8) -> bool> {
allowed: [bool; 256],
predicate: Option<F>,
min_length: usize,
max_length: Option<usize>,
}
impl Class<fn(u8) -> bool> {
pub fn new(bytes: &[u8]) -> Self {
let mut allowed = [false; 256];
for &byte in bytes {
allowed[byte as usize] = true;
}
Self {
allowed,
predicate: None,
min_length: 0,
max_length: None,
}
}
pub fn with_min(bytes: &[u8], min_length: usize) -> Self {
let mut allowed = [false; 256];
for &byte in bytes {
allowed[byte as usize] = true;
}
Self {
allowed,
predicate: None,
min_length,
max_length: None,
}
}
pub fn with_max(bytes: &[u8], max_length: usize) -> Self {
let mut allowed = [false; 256];
for &byte in bytes {
allowed[byte as usize] = true;
}
Self {
allowed,
predicate: None,
min_length: 0,
max_length: Some(max_length),
}
}
pub fn with_bounds(bytes: &[u8], min_length: usize, max_length: usize) -> Self {
let mut allowed = [false; 256];
for &byte in bytes {
allowed[byte as usize] = true;
}
Self {
allowed,
predicate: None,
min_length,
max_length: Some(max_length),
}
}
pub fn digits() -> Self {
Self::with_min(b"0123456789", 1)
}
pub fn alpha() -> Self {
Self::with_min(b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 1)
}
pub fn alphanumeric() -> Self {
Self::with_min(
b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
1,
)
}
pub fn whitespace() -> Self {
Self::with_min(b" \t\r\n\x0b\x0c", 1)
}
pub fn hex_digits() -> Self {
Self::with_min(b"0123456789abcdefABCDEF", 1)
}
}
impl<F> Class<F>
where
F: Fn(u8) -> bool,
{
pub fn from_predicate(predicate: F) -> Self {
Self {
allowed: [false; 256], predicate: Some(predicate),
min_length: 0,
max_length: None,
}
}
pub fn from_predicate_min(predicate: F, min_length: usize) -> Self {
Self {
allowed: [false; 256],
predicate: Some(predicate),
min_length,
max_length: None,
}
}
pub fn from_predicate_max(predicate: F, max_length: usize) -> Self {
Self {
allowed: [false; 256],
predicate: Some(predicate),
min_length: 0,
max_length: Some(max_length),
}
}
pub fn from_predicate_bounds(predicate: F, min_length: usize, max_length: usize) -> Self {
Self {
allowed: [false; 256],
predicate: Some(predicate),
min_length,
max_length: Some(max_length),
}
}
fn byte_matches(&self, byte: u8) -> bool {
if let Some(ref predicate) = self.predicate {
predicate(byte)
} else {
self.allowed[byte as usize]
}
}
}
impl<F, Ctx> Parser<Ctx> for Class<F>
where
F: Fn(u8) -> bool + 'static,
{
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.allowed.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_length) = self.max_length {
if result.len() >= max_length {
break;
}
}
match source.peek1() {
Ok(byte) => {
if self.byte_matches(byte) {
result.push(byte);
source.advance(1);
} else {
break;
}
}
Err(Error::NoMatch) => {
break;
}
Err(err) => return Err(err),
}
}
if result.len() < self.min_length {
return Err(Error::NoMatch);
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::parse;
use std::io::Cursor;
#[test]
fn test_id_implementation_different_class_parsers() {
let class1 = Class::new(b"abc");
let class2 = Class::new(b"xyz");
assert_ne!(
<Class as crate::parser::Parser<()>>::id(&class1),
<Class as crate::parser::Parser<()>>::id(&class2),
"Different Class instances should have different IDs to avoid cache collisions"
);
}
#[test]
fn test_id_implementation_same_class_parsers() {
let class1 = Class::new(b"abc");
let class2 = Class::new(b"abc");
assert_eq!(
<Class as crate::parser::Parser<()>>::id(&class1),
<Class as crate::parser::Parser<()>>::id(&class2),
"Identical Class instances should have the same ID for cache efficiency"
);
}
#[test]
fn test_id_implementation_class_different_bounds() {
let class1 = Class::with_min(b"abc", 1);
let class2 = Class::with_min(b"abc", 2);
assert_ne!(
<Class as crate::parser::Parser<()>>::id(&class1),
<Class as crate::parser::Parser<()>>::id(&class2),
"Class instances with different bounds should have different IDs"
);
}
#[test]
fn test_id_implementation_class_predicate_parsers() {
let class1 = Class::from_predicate(|b| b.is_ascii_alphabetic());
let class2 = Class::from_predicate(|b| b.is_ascii_digit());
fn get_id<P: crate::parser::Parser<()>>(parser: &P) -> u64 {
parser.id()
}
assert_ne!(
get_id(&class1),
get_id(&class2),
"Class instances with different predicates should have different IDs"
);
}
#[test]
fn test_class_basic_functionality() {
let class = Class::new(b"abc");
let mut input = Cursor::new(b"aabbcc123");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(class, &mut source).unwrap();
assert_eq!(result, b"aabbcc".to_vec());
}
#[test]
fn test_class_empty_character_set() {
let class = Class::new(b"");
let mut input = Cursor::new(b"abc");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(class, &mut source).unwrap();
assert_eq!(result, Vec::<u8>::new()); }
#[test]
fn test_class_boundary_conditions() {
let class = Class::with_min(b"abc", 3);
let mut input1 = Cursor::new(b"abc");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(class, &mut source1).unwrap();
assert_eq!(result1, b"abc".to_vec());
let class2 = Class::with_min(b"abc", 4);
let mut input2 = Cursor::new(b"abc");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(class2, &mut source2);
assert!(result2.is_err());
let class3 = Class::with_max(b"abc", 2);
let mut input3 = Cursor::new(b"abcabc");
let mut source3 = crate::parser::Source::new(&mut input3);
let result3 = parse(class3, &mut source3).unwrap();
assert_eq!(result3, b"ab".to_vec());
}
#[test]
fn test_class_non_ascii_bytes() {
let class = Class::new(&[0xFF, 0xFE, 0xFD]);
let mut input = Cursor::new(&[0xFF, 0xFE, 0xFD, 0xFC, 0x00]);
let mut source = crate::parser::Source::new(&mut input);
let result = parse(class, &mut source).unwrap();
assert_eq!(result, vec![0xFF, 0xFE, 0xFD]);
}
#[test]
fn test_class_position_tracking() {
let class = Class::new(b"abc");
let mut input = Cursor::new(b"abcXYZ");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(class, &mut source).unwrap();
assert_eq!(result, b"abc".to_vec());
let next_byte = source.peek1().unwrap();
assert_eq!(next_byte, b'X');
}
#[test]
fn test_class_convenience_constructors() {
let digits = Class::digits();
let mut input1 = Cursor::new(b"123abc");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(digits, &mut source1).unwrap();
assert_eq!(result1, b"123".to_vec());
let alpha = Class::alpha();
let mut input2 = Cursor::new(b"Hello123");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(alpha, &mut source2).unwrap();
assert_eq!(result2, b"Hello".to_vec());
let alnum = Class::alphanumeric();
let mut input3 = Cursor::new(b"Hello123_world");
let mut source3 = crate::parser::Source::new(&mut input3);
let result3 = parse(alnum, &mut source3).unwrap();
assert_eq!(result3, b"Hello123".to_vec());
}
#[test]
fn test_class_predicate_edge_cases() {
let always_true = Class::from_predicate(|_| true);
let mut input1 = Cursor::new(b"abc");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(always_true, &mut source1).unwrap();
assert_eq!(result1, b"abc".to_vec());
let always_false = Class::from_predicate(|_| false);
let mut input2 = Cursor::new(b"abc");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(always_false, &mut source2).unwrap();
assert_eq!(result2, Vec::<u8>::new());
let always_false_min = Class::from_predicate_min(|_| false, 1);
let mut input3 = Cursor::new(b"abc");
let mut source3 = crate::parser::Source::new(&mut input3);
let result3 = parse(always_false_min, &mut source3);
assert!(result3.is_err());
}
#[test]
fn test_class_empty_input() {
let class = Class::new(b"abc");
let mut input = Cursor::new(b"");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(class, &mut source).unwrap();
assert_eq!(result, Vec::<u8>::new());
let class_min = Class::with_min(b"abc", 1);
let mut input2 = Cursor::new(b"");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(class_min, &mut source2);
assert!(result2.is_err());
}
#[test]
fn test_class_large_character_set() {
let all_bytes: Vec<u8> = (0..=255).collect();
let class = Class::new(&all_bytes);
let mut input = Cursor::new(b"Hello\xFF\xFE\xFD123");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(class, &mut source).unwrap();
assert_eq!(result, b"Hello\xFF\xFE\xFD123".to_vec());
}
}