use std::{ io::{ BufRead, ErrorKind }, str::from_utf8 };
use crate::language::text::{ buffer::Buffer, location::Location };
pub struct ReadBuffer<'a, R: BufRead>
{
reader: &'a mut R,
location: Location,
tab_width: usize,
current_char: Option<char>,
exhausted: bool,
error: Option<String>
}
impl<'a, R: BufRead> ReadBuffer<'a, R>
{
pub fn new(origin: &str, reader: &'a mut R, tab_width: Option<usize>) -> Self
{
Self
{
reader,
location: Location::new(origin, 1, 1),
tab_width: tab_width.unwrap_or(4),
current_char: None,
exhausted: false,
error: None
}
}
fn read_char(&mut self) -> Option<char>
{
if self.exhausted { return None; }
let mut buffer = [0; 4];
for length in 1..=buffer.len()
{
if let Err(error) = self.reader.read_exact(&mut buffer[length - 1..length])
{
self.exhausted = true;
if error.kind() != ErrorKind::UnexpectedEof || length != 1
{
self.error = Some(format!("Failed to read UTF-8 source: {}", error));
}
return None;
}
match from_utf8(&buffer[..length])
{
Ok(text) => return text.chars().next(),
Err(error) if error.error_len().is_none() => continue,
Err(error) =>
{
self.exhausted = true;
self.error = Some(format!("Invalid UTF-8 source: {}", error));
return None;
}
}
}
None
}
}
impl<'a, R: BufRead> Buffer for ReadBuffer<'a, R>
{
fn read_error(&self) -> Option<&str> { self.error.as_deref() }
fn location(&self) -> &Location
{
&self.location
}
fn location_mut(&mut self) -> &mut Location
{
&mut self.location
}
fn peek_next(&mut self) -> Option<char>
{
match self.current_char
{
Some(_) => self.current_char,
None =>
{
let next = self.read_char();
self.current_char = next;
next
}
}
}
fn next(&mut self) -> Option<char>
{
let next = self.current_char.take().or_else(|| self.read_char());
if let Some(next) = next
{
self.increment_location(next, self.tab_width);
}
next
}
}