use crate::terminal::error::{self, TerminalError}; use std::io::{BufRead, BufReader, Read}; use std::str; pub struct CharBufReader { byte_reader: BufReader, char_buffer: String, } impl CharBufReader { pub fn new(input_stream: R) -> CharBufReader { let byte_reader = BufReader::new(input_stream); CharBufReader { byte_reader: byte_reader, char_buffer: String::new(), } } pub fn fill_buf(&mut self) -> std::result::Result<&str, TerminalError> { loop { let byte_buffer = self.byte_reader.fill_buf().map_err(error::input)?; match str::from_utf8(byte_buffer) { Err(error) => { let n_valid = error.valid_up_to(); if n_valid == 0 { self.byte_reader.consume(1); } else { match str::from_utf8(&byte_buffer[..n_valid]) { Err(_) => { self.byte_reader.consume(1); }, Ok(chars) => { self.char_buffer.push_str(chars); self.byte_reader.consume(n_valid); break; }, } } }, Ok(chars) => { self.char_buffer.push_str(chars); let n_to_consume = byte_buffer.len(); self.byte_reader.consume(n_to_consume); break; } } } return Ok(&self.char_buffer); } pub fn consume(&mut self, amount: usize) { self.char_buffer.replace_range(..amount, ""); } }