use crate::io::traits::{ICharStream, IIndentationAware, IStatefulStream};
impl ICharStream for Buffer {
fn next(&mut self) {
if !self.more() {
return;
}
let current_byte = self.buffer[self.position];
self.position += 1;
match current_byte {
b'\n' => {
self.line += 1;
self.column = 0;
}
b'\r' => {
if self.more() && self.buffer[self.position] == b'\n' {
} else {
self.line += 1;
self.column = 0;
}
}
_ => {
self.column += 1;
}
}
}
fn current(&mut self) -> Option<char> {
if self.more() {
Some(self.buffer[self.position] as char)
} else {
None
}
}
fn more(&mut self) -> bool {
self.position < self.buffer.len()
}
fn reset(&mut self) {
self.position = 0;
}
}
impl IIndentationAware for Buffer {
fn get_current_indent_level(&self) -> usize {
self.column
}
}
impl IStatefulStream for Buffer {
fn save_state(&mut self) -> crate::io::traits::SaveState {
crate::io::traits::SaveState {
pos: self.position as u64,
current_byte: if self.more() {
Some(self.buffer[self.position])
} else {
None
},
column: self.column,
line: self.line,
}
}
fn restore_state(&mut self, state: crate::io::traits::SaveState) {
self.position = state.pos as usize;
self.column = state.column;
self.line = state.line;
}
}
pub struct Buffer {
buffer: Vec<u8>,
position: usize,
column: usize,
line: usize,
}
impl Buffer {
pub fn new(to_add: &[u8]) -> Self {
Self {
buffer: to_add.to_vec(),
position: 0,
column: 0,
line: 0,
}
}
pub fn to_string(&self) -> String {
String::from_utf8_lossy(&self.buffer).into_owned()
}
pub fn next(&mut self) {
ICharStream::next(self)
}
pub fn current(&mut self) -> Option<char> {
ICharStream::current(self)
}
pub fn more(&mut self) -> bool {
ICharStream::more(self)
}
pub fn reset(&mut self) {
ICharStream::reset(self)
}
pub fn get_current_indent_level(&self) -> usize {
IIndentationAware::get_current_indent_level(self)
}
pub fn save_state(&mut self) -> crate::io::traits::SaveState {
IStatefulStream::save_state(self)
}
pub fn restore_state(&mut self, state: crate::io::traits::SaveState) {
IStatefulStream::restore_state(self, state)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_source_buffer_works() {
let source = Buffer::new(String::from("i32e").as_bytes());
assert_eq!(source.to_string(), "i32e");
}
#[test]
fn read_character_from_source_buffer_works() {
let mut source = Buffer::new(String::from("i32e").as_bytes());
match source.current() {
Some('i') => assert!(true),
_ => assert!(false),
}
}
#[test]
fn move_to_next_character_in_source_buffer_works() {
let mut source = Buffer::new(String::from("i32e").as_bytes());
source.next();
match source.current() {
Some('3') => assert!(true),
_ => assert!(false),
}
}
#[test]
fn move_to_last_character_in_source_buffer_works() {
let mut source = Buffer::new(String::from("i32e").as_bytes());
while source.more() {
source.next()
}
match source.current() {
None => assert!(true),
_ => assert!(false),
}
}
#[test]
fn reset_in_source_buffer_works() {
let mut source = Buffer::new(String::from("i32e").as_bytes());
while source.more() {
source.next()
}
source.reset();
match source.current() {
Some('i') => assert!(true),
_ => assert!(false),
}
}
#[test]
fn create_empty_buffer_works() {
let source = Buffer::new(&[]);
assert_eq!(source.to_string(), "");
}
#[test]
fn handle_non_utf8_content() {
let source = Buffer::new(&[0xFF]);
assert_eq!(source.to_string(), String::from_utf8_lossy(&[0xFF]));
}
#[test]
fn more_returns_correct_at_boundaries() {
let mut source = Buffer::new(String::from("a").as_bytes());
assert!(source.more());
source.next();
assert!(!source.more());
}
#[test]
fn multiple_next_calls_work() {
let mut source = Buffer::new(String::from("abc").as_bytes());
source.next();
source.next();
match source.current() {
Some('c') => assert!(true),
_ => assert!(false),
}
}
#[test]
fn save_restore_works() {
let mut source = Buffer::new(String::from("abc").as_bytes());
let s0 = source.save_state();
source.next();
source.restore_state(s0);
match source.current() {
Some('a') => assert!(true),
_ => assert!(false),
}
source.next();
let s = source.save_state();
source.next();
assert_eq!(source.current(), Some('c'));
source.restore_state(s);
assert_eq!(source.current(), Some('b'));
}
#[test]
fn get_current_indent_level_works() {
let mut source = Buffer::new(String::from(" abc").as_bytes());
source.next();
source.next();
assert_eq!(source.get_current_indent_level(), 2);
}
#[test]
fn buffer_eof_after_consumption() {
let mut source = Buffer::new(String::from("xy").as_bytes());
source.next();
source.next();
assert_eq!(source.current(), None);
assert!(!source.more());
}
#[test]
fn buffer_next_safe_at_eof() {
let mut source = Buffer::new(String::from("a").as_bytes());
assert_eq!(source.current(), Some('a'));
source.next();
assert_eq!(source.current(), None);
source.next();
assert_eq!(source.current(), None);
assert!(!source.more());
}
#[test]
fn buffer_reset_restores_after_eof() {
let mut source = Buffer::new(String::from("z").as_bytes());
assert_eq!(source.current(), Some('z'));
source.next();
assert_eq!(source.current(), None);
source.reset();
assert_eq!(source.current(), Some('z'));
assert!(source.more());
}
#[test]
fn buffer_handles_only_newlines() {
let mut source = Buffer::new(b"\n\n\n");
assert_eq!(source.line, 0);
assert_eq!(source.column, 0);
for i in 0..3 {
assert_eq!(source.current(), Some('\n'));
source.next();
assert_eq!(source.line, i + 1);
assert_eq!(source.column, 0);
}
assert_eq!(source.current(), None);
}
#[test]
fn buffer_handles_mixed_line_endings() {
let mut source = Buffer::new(b"a\r\nb\rc\nd");
assert_eq!(source.current(), Some('a'));
source.next(); assert_eq!(source.current(), Some('\r'));
source.next(); assert_eq!(source.current(), Some('\n'));
source.next(); assert_eq!(source.current(), Some('b'));
source.next(); assert_eq!(source.current(), Some('\r'));
source.next(); assert_eq!(source.current(), Some('c'));
source.next(); assert_eq!(source.current(), Some('\n'));
source.next(); assert_eq!(source.current(), Some('d'));
source.next();
assert_eq!(source.current(), None);
}
#[test]
fn buffer_save_restore_multiple_points() {
let mut source = Buffer::new(b"abcde");
let s0 = source.save_state();
source.next();
let s1 = source.save_state();
source.next();
let s2 = source.save_state();
source.next();
assert_eq!(source.current(), Some('d'));
source.restore_state(s1);
assert_eq!(source.current(), Some('b'));
source.restore_state(s2);
assert_eq!(source.current(), Some('c'));
source.restore_state(s0);
assert_eq!(source.current(), Some('a'));
}
#[test]
fn buffer_reset_after_partial_read() {
let mut source = Buffer::new(b"xyz");
source.next();
assert_eq!(source.current(), Some('y'));
source.reset();
assert_eq!(source.current(), Some('x'));
}
#[test]
fn buffer_handles_large_input() {
let data = vec![b'a'; 10_000];
let mut source = Buffer::new(&data);
let mut count = 0;
while source.more() {
assert_eq!(source.current(), Some('a'));
source.next();
count += 1;
}
assert_eq!(count, 10_000);
}
}