use std::char;
use std::str;
use std::cmp;
use {
StreamError,
StrSpan,
TextPos,
};
type Result<T> = ::std::result::Result<T, StreamError>;
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct ByteStream<'a> {
pos: usize,
end: usize,
span: StrSpan<'a>,
}
impl<'a> ByteStream<'a> {
pub(crate) fn new(span: StrSpan<'a>) -> Self {
ByteStream {
pos: 0,
end: span.as_str().len(),
span,
}
}
#[inline]
pub fn span(&self) -> StrSpan<'a> {
self.span
}
#[inline]
pub fn pos(&self) -> usize {
self.pos
}
#[inline]
pub fn jump_to_end(&mut self) {
self.pos = self.end;
}
#[inline]
pub fn at_end(&self) -> bool {
self.pos >= self.end
}
#[inline]
pub fn curr_byte(&self) -> Result<u8> {
if self.at_end() {
return Err(StreamError::UnexpectedEndOfStream);
}
Ok(self.curr_byte_unchecked())
}
#[inline]
pub fn curr_byte_unchecked(&self) -> u8 {
self.span.as_bytes()[self.pos]
}
#[inline]
pub fn next_byte(&self) -> Result<u8> {
if self.pos + 1 >= self.end {
return Err(StreamError::UnexpectedEndOfStream);
}
Ok(self.span.as_bytes()[self.pos + 1])
}
#[inline]
pub fn advance(&mut self, n: usize) {
debug_assert!(self.pos + n <= self.end);
self.pos += n;
}
#[inline]
pub fn starts_with(&self, text: &[u8]) -> bool {
self.span.as_bytes()[self.pos..self.end].starts_with(text)
}
pub fn consume_byte(&mut self, c: u8) -> Result<()> {
if self.curr_byte()? != c {
return Err(
StreamError::InvalidChar(
vec![self.curr_byte_unchecked(), c],
self.gen_text_pos(),
)
);
}
self.advance(1);
Ok(())
}
pub fn try_consume_byte(&mut self, c: u8) -> bool {
match self.curr_byte() {
Ok(b) if b == c => {
self.advance(1);
true
}
_ => false,
}
}
pub fn skip_string(&mut self, text: &[u8]) -> Result<()> {
if !self.starts_with(text) {
let len = cmp::min(text.len(), self.end - self.pos);
let actual = self.span.as_str()[self.pos..].chars().take(len).collect();
let expected = str::from_utf8(text).unwrap().to_owned();
let pos = self.gen_text_pos();
return Err(StreamError::InvalidString(vec![actual, expected], pos));
}
self.advance(text.len());
Ok(())
}
pub fn consume_bytes<F>(&mut self, f: F) -> StrSpan<'a>
where F: Fn(&ByteStream, u8) -> bool
{
let start = self.pos;
self.skip_bytes(f);
self.slice_back(start)
}
pub fn skip_bytes<F>(&mut self, f: F)
where F: Fn(&ByteStream, u8) -> bool
{
while !self.at_end() && f(self, self.curr_byte_unchecked()) {
self.advance(1);
}
}
pub fn consume_chars<F>(&mut self, f: F) -> StrSpan<'a>
where F: Fn(&ByteStream, char) -> bool
{
let start = self.pos;
self.skip_chars(f);
self.slice_back(start)
}
pub fn skip_chars<F>(&mut self, f: F)
where F: Fn(&ByteStream, char) -> bool
{
for c in self.chars() {
if f(self, c) {
self.advance(c.len_utf8());
} else {
break;
}
}
}
#[inline]
pub(crate) fn chars(&self) -> str::Chars<'a> {
self.span.as_str()[self.pos..self.end].chars()
}
#[inline]
pub fn slice_back(&self, pos: usize) -> StrSpan<'a> {
self.span.slice_region(pos, self.pos)
}
#[inline]
pub fn slice_tail(&self) -> StrSpan<'a> {
self.span.slice_region(self.pos, self.end)
}
#[inline(never)]
pub fn gen_text_pos(&self) -> TextPos {
let text = self.span.full_str();
let end = self.pos + self.span.start();
let row = Self::calc_curr_row(text, end);
let col = Self::calc_curr_col(text, end);
TextPos::new(row, col)
}
#[inline(never)]
pub fn gen_text_pos_from(&self, pos: usize) -> TextPos {
let mut s = self.clone();
s.pos = cmp::min(pos, s.span.full_str().len());
s.gen_text_pos()
}
fn calc_curr_row(text: &str, end: usize) -> u32 {
let mut row = 1;
for c in &text.as_bytes()[..end] {
if *c == b'\n' {
row += 1;
}
}
row
}
fn calc_curr_col(text: &str, end: usize) -> u32 {
let mut col = 1;
for c in text[..end].chars().rev() {
if c == '\n' {
break;
} else {
col += 1;
}
}
col
}
}