perl-regex 0.16.0

Regex parsing and validation helpers for Perl syntax
Documentation
pub(crate) fn quoted_literal_end(bytes: &[u8], start: usize) -> Option<usize> {
    if bytes.get(start) != Some(&b'\\') || bytes.get(start + 1) != Some(&b'Q') {
        return None;
    }

    let mut i = start + 2;
    while i + 1 < bytes.len() {
        if bytes[i] == b'\\' && bytes[i + 1] == b'E' {
            return Some(i + 2);
        }
        i += 1;
    }
    Some(bytes.len())
}

pub(crate) struct RegexCursor<'a> {
    bytes: &'a [u8],
    pos: usize,
}

impl<'a> RegexCursor<'a> {
    pub(crate) fn new(pattern: &'a str) -> Self {
        Self { bytes: pattern.as_bytes(), pos: 0 }
    }

    pub(crate) fn current(&self) -> Option<u8> {
        self.bytes.get(self.pos).copied()
    }

    pub(crate) fn peek(&self, offset: usize) -> Option<u8> {
        self.bytes.get(self.pos + offset).copied()
    }

    pub(crate) fn bump(&mut self) {
        self.pos += 1;
    }

    pub(crate) fn position(&self) -> usize {
        self.pos
    }

    pub(crate) fn skip_quoted_literal(&mut self) -> bool {
        if let Some(end) = quoted_literal_end(self.bytes, self.pos) {
            self.pos = end;
            return true;
        }
        false
    }

    pub(crate) fn skip_escape(&mut self) -> bool {
        if self.current() == Some(b'\\') {
            self.pos += 2;
            return true;
        }
        false
    }

    pub(crate) fn skip_char_class(&mut self) -> bool {
        if self.current() != Some(b'[') {
            return false;
        }
        self.pos += 1;
        while let Some(ch) = self.current() {
            if ch == b'\\' {
                self.pos += 2;
            } else if ch == b']' {
                self.pos += 1;
                break;
            } else {
                self.pos += 1;
            }
        }
        true
    }
}