use crate::pattern::Pattern;
use core::ops::Deref;
#[derive(Debug, PartialEq, Eq)]
pub struct Buf<'b> {
inner: &'b [u8],
pos: usize,
}
impl<'b> Buf<'b> {
pub fn rest(&self) -> &'b [u8] {
&self.inner[self.pos..]
}
pub fn consume(&mut self, pattern: impl Pattern) -> Option<&'b [u8]> {
let offset = pattern.eval(self.rest())?;
let result = &self.rest()[..offset];
self.pos += offset;
Some(result)
}
pub fn peek(&self, pattern: impl Pattern) -> Option<&'b [u8]> {
let offset = pattern.eval(self.rest())?;
Some(&self.rest()[..offset])
}
pub fn consume_until(&mut self, marker: impl Pattern) -> Option<&'b [u8]> {
let mut offset = 0;
loop {
let rest = &self.rest()[offset..];
if marker.eval(rest).is_some() {
let result = &self.rest()[..offset];
self.pos += offset;
return Some(result);
}
if rest.is_empty() {
return None;
}
offset += 1;
}
}
pub fn consume_until_after(&mut self, marker: impl Pattern) -> Option<&'b [u8]> {
let mut offset = 0;
loop {
let rest = &self.rest()[offset..];
if let Some(v) = marker.eval(rest) {
offset += v;
let result = &self.rest()[..offset];
self.pos += offset;
return Some(result);
}
if rest.is_empty() {
return None;
}
offset += 1;
}
}
pub fn skip(&mut self, pattern: impl Pattern) -> bool {
self.consume(pattern).is_some()
}
pub fn matches(&self, pattern: impl Pattern) -> bool {
self.peek(pattern).is_some()
}
pub fn position(&self) -> usize {
self.pos
}
pub fn advance(&mut self, step: usize) {
let remaining = self.inner.len() - self.pos;
let step = std::cmp::min(remaining, step);
self.pos += step;
}
}
impl<'b> From<&'b [u8]> for Buf<'b> {
fn from(value: &'b [u8]) -> Self {
Buf {
pos: 0,
inner: value,
}
}
}
impl<'b> From<&'b str> for Buf<'b> {
fn from(value: &'b str) -> Self {
Buf::from(value.as_bytes())
}
}
impl Deref for Buf<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.inner
}
}
impl AsRef<[u8]> for Buf<'_> {
fn as_ref(&self) -> &[u8] {
self.inner
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pattern;
#[test]
fn deref() {
let mut buf = Buf::from("aba");
assert!(buf.skip("aba"));
assert!(buf.matches(pattern::end()));
assert_eq!(&*buf, b"aba");
}
#[test]
fn parse() {
let pattern = "a".or("b");
let mut buf = Buf::from("abab");
assert_eq!(buf.consume(pattern).as_deref(), Some(b"a" as &[u8]));
assert_eq!(buf.consume(pattern).as_deref(), Some(b"b" as &[u8]));
assert_eq!(buf.consume(pattern).as_deref(), Some(b"a" as &[u8]));
assert_eq!(buf.consume(pattern).as_deref(), Some(b"b" as &[u8]));
assert_eq!(buf.consume(pattern), None);
assert_eq!(buf.consume(pattern), None);
assert_eq!(buf.consume(pattern), None);
}
#[test]
fn skip() {
let ws = " ";
let mut buf = Buf::from(" a");
assert!(buf.skip(ws));
assert!(buf.skip(ws));
assert!(buf.skip(ws));
assert!(!buf.skip(ws));
assert!(buf.skip("a"));
}
#[test]
fn consume_until() {
let mut buf = Buf::from("");
assert_eq!(buf.consume_until(">"), None);
let mut buf = Buf::from("<tag>");
assert_eq!(buf.consume_until(">"), Some(b"<tag" as &[u8]));
assert_eq!(buf.consume_until(">"), Some(&[] as &[u8]));
assert_eq!(buf.consume_until(pattern::end()), Some(b">" as &[u8]));
}
#[test]
fn consume_until_after() {
let mut buf = Buf::from("");
assert_eq!(buf.consume_until_after(">"), None);
let mut buf = Buf::from("<tag>");
assert_eq!(buf.consume_until_after(">"), Some(b"<tag>" as &[u8]));
assert_eq!(buf.consume_until_after(pattern::end()), Some(&[] as &[u8]));
}
#[test]
fn advance() {
let mut buf = Buf::from("");
assert!(buf.skip(pattern::end()));
buf.advance(10);
assert!(buf.skip(pattern::end()));
assert_eq!(buf.position(), 0);
let mut buf = Buf::from("abc");
assert_eq!(buf.position(), 0);
buf.advance(2);
assert!(buf.matches("c"));
}
}