pub struct CharBuffer {
buf: Vec<u8>,
}
impl CharBuffer {
pub fn new() -> Self {
CharBuffer { buf: vec![] }
}
pub fn from_vec(buf: Vec<u8>) -> Self {
CharBuffer { buf }
}
pub fn pop_char(&mut self) -> Option<u8> {
self.buf.pop()
}
pub fn push_char(&mut self, c: u8) {
self.buf.push(c);
}
pub fn clear(&mut self) {
self.buf.clear();
}
fn count_trailing_spaces(&self) -> usize {
self.buf.iter().rev().take_while(|&&b| b == b' ').count()
}
pub fn peek_word(&self) -> Option<&[u8]> {
let trailing_spaces = self.count_trailing_spaces();
if self.buf.len() <= trailing_spaces {
return None;
}
let mut start = self.buf.len() - trailing_spaces;
while start > 0 && self.buf[start - 1] != b' ' {
start -= 1;
}
Some(&self.buf[start..self.buf.len() - trailing_spaces])
}
pub fn pop_word(&mut self) -> Option<Vec<u8>> {
let word_slice = self.peek_word()?;
let word = word_slice.to_vec();
let trailing_spaces = self.count_trailing_spaces();
let new_len = self.buf.len().saturating_sub(word.len() + trailing_spaces);
self.buf.truncate(new_len);
Some(word)
}
pub fn peek_char(&self) -> Option<&u8> {
self.buf.last()
}
pub fn get_buf(&self) -> &[u8] {
&self.buf
}
}
impl Default for CharBuffer {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod test {
use super::CharBuffer;
#[test]
fn test_count_trailing_spaces() {
let mut buf = CharBuffer::from_vec(b"hello world ".to_vec());
assert_eq!(buf.count_trailing_spaces(), 3);
buf.clear();
assert_eq!(buf.count_trailing_spaces(), 0);
buf = CharBuffer::from_vec(b" ".to_vec());
assert_eq!(buf.count_trailing_spaces(), 3);
}
#[test]
fn pop_char_returns_last_byte() {
let mut buf = CharBuffer::from_vec(vec![b'a', b'b', b'c']);
assert_eq!(buf.pop_char(), Some(b'c'));
assert_eq!(buf.pop_char(), Some(b'b'));
assert_eq!(buf.pop_char(), Some(b'a'));
assert_eq!(buf.pop_char(), None);
}
#[test]
fn peek_word() {
let buf = CharBuffer::from_vec(b"test ".to_vec());
assert_eq!(buf.peek_word(), Some(&b"test"[..]));
assert_eq!(buf.get_buf(), b"test ");
}
#[test]
fn pop_word_truncates_at_last_space() {
let mut buf = CharBuffer::from_vec(b"hello world test".to_vec());
assert_eq!(buf.pop_word(), Some(b"test".to_vec())); assert_eq!(buf.get_buf(), b"hello world "); assert_eq!(buf.pop_word(), Some(b"world".to_vec())); assert_eq!(buf.get_buf(), b"hello "); assert_eq!(buf.pop_word(), Some(b"hello".to_vec())); assert_eq!(buf.get_buf(), b"");
}
#[test]
fn pop_word_empty_buffer() {
let mut buf = CharBuffer::new();
assert_eq!(buf.pop_word(), None);
}
#[test]
fn push_char_and_get_buf() {
let mut buf = CharBuffer::new();
buf.push_char(b'a');
buf.push_char(b'b');
buf.push_char(b'c');
assert_eq!(buf.get_buf(), b"abc");
}
#[test]
fn clear_resets_buffer() {
let mut buf = CharBuffer::from_vec(b"something".to_vec());
buf.clear();
assert_eq!(buf.get_buf(), b"");
assert_eq!(buf.pop_char(), None);
}
#[test]
fn peek_char_returns_last_without_removing() {
let mut buf = CharBuffer::from_vec(vec![b'x', b'y']);
assert_eq!(buf.peek_char(), Some(&b'y'));
assert_eq!(buf.get_buf(), b"xy");
buf.pop_char();
assert_eq!(buf.peek_char(), Some(&b'x'));
}
#[test]
fn peek_char_empty_buffer() {
let buf = CharBuffer::new();
assert_eq!(buf.peek_char(), None);
}
#[test]
fn from_vec_constructor_works() {
let buf = CharBuffer::from_vec(b"abc".to_vec());
assert_eq!(buf.get_buf(), b"abc");
}
#[test]
fn peek_and_pop_word() {
let mut buf = CharBuffer::from_vec(b"hello world test".to_vec());
assert_eq!(buf.peek_word(), Some(&b"test"[..]));
assert_eq!(buf.get_buf(), b"hello world test");
assert_eq!(buf.pop_word(), Some(b"test".to_vec()));
assert_eq!(buf.get_buf(), b"hello world ");
assert_eq!(buf.peek_word(), Some(&b"world"[..]));
assert_eq!(buf.get_buf(), b"hello world ");
let mut buf3 = CharBuffer::new();
assert_eq!(buf3.peek_word(), None);
assert_eq!(buf3.pop_word(), None);
let mut buf4 = CharBuffer::from_vec(b" ".to_vec());
assert_eq!(buf4.peek_word(), None);
assert_eq!(buf4.pop_word(), None);
}
}