From b0532856d5d16aeaaebc8726656ff207c9c57ce0 Mon Sep 17 00:00:00 2001 From: trivernis Date: Fri, 23 Apr 2021 09:11:45 +0200 Subject: [PATCH] Add InputReader Signed-off-by: trivernis --- Cargo.toml | 12 +- src/error.rs | 9 + src/input.rs | 50 ++++++ src/lib.rs | 85 +-------- src/tapemachine.rs | 424 --------------------------------------------- src/traits.rs | 3 + 6 files changed, 76 insertions(+), 507 deletions(-) create mode 100644 src/error.rs create mode 100644 src/input.rs delete mode 100644 src/tapemachine.rs create mode 100644 src/traits.rs diff --git a/Cargo.toml b/Cargo.toml index eea0eac..83af892 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "charred" -version = "0.3.6" +version = "2.0.0" authors = ["trivernis "] edition = "2018" license-file = "LICENSE" @@ -12,3 +12,13 @@ repository = "https://github.com/Trivernis/charred-rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +thiserror = "1.0.24" +log = "0.4.14" + +[dependencies.tokio] +version = "1.5.0" +features = ["io-util", "io-std"] + +[dev-dependencies.tokio] +version = "1.5.0" +features = ["rt-multi-thread", "test-util"] \ No newline at end of file diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..4a21335 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,9 @@ +use thiserror::Error; + +pub type TapeResult = Result; + +#[derive(Debug, Error)] +pub enum TapeError { + #[error("IO Error: {0}")] + TokioIoError(#[from] tokio::io::Error), +} diff --git a/src/input.rs b/src/input.rs new file mode 100644 index 0000000..a1ee8df --- /dev/null +++ b/src/input.rs @@ -0,0 +1,50 @@ +use crate::error::TapeResult; +use crate::traits::AsyncReadSeek; +use std::io::SeekFrom; +use tokio::io::{AsyncReadExt, BufReader}; +use tokio::io::{AsyncSeekExt, ErrorKind}; + +/// An Input reader to asynchronously read a type +/// that implements AsyncBufRead and AsyncSeek. +pub struct InputReader { + inner: BufReader>, +} + +impl InputReader { + pub fn new(inner: T) -> Self { + Self { + inner: BufReader::new(Box::new(inner)), + } + } + + /// Reads the next char consuming it in the process + pub async fn consume(&mut self) -> TapeResult { + self.read_next().await + } + + /// Returns the next char without forwarding + pub async fn peek(&mut self) -> TapeResult { + let char = self.read_next().await?; + self.inner.get_mut().seek(SeekFrom::Current(-1)).await?; + + Ok(char) + } + + /// Returns if EOF has been reached + pub async fn check_eof(&mut self) -> TapeResult { + let char = self.read_next().await?; + + Ok(char == '\x00') + } + + /// Reads the next char returning \x00 for EOF + async fn read_next(&mut self) -> TapeResult { + let mut value = String::with_capacity(1); + + match self.inner.read_to_string(&mut value).await { + Ok(_) => Ok(value.chars().next().unwrap()), + Err(e) if e.kind() == ErrorKind::UnexpectedEof => Ok('\x00'), + Err(e) => Err(e.into()), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index d7c7077..b8c2773 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,82 +1,3 @@ -pub mod tapemachine; - -#[cfg(test)] -mod tests { - use crate::tapemachine::TapeResult; - use crate::tapemachine::{CharTapeMachine, TapeError}; - - const TEST_STRING: &str = "TEST STRING 1234 \\l \\n"; - - #[test] - fn it_returns_the_next_char() { - let mut ctm = CharTapeMachine::new(TEST_STRING.chars().collect()); - let test_chars: Vec = TEST_STRING.chars().collect(); - - let mut next = ctm.next_char().unwrap(); - assert_eq!(next, *test_chars.get(1).unwrap()); - - next = ctm.next_char().unwrap(); - assert_eq!(next, *test_chars.get(2).unwrap()); - - let _ = ctm.next_char().unwrap(); - let _ = ctm.next_char().unwrap(); - let _ = ctm.next_char().unwrap(); - next = ctm.next_char().unwrap(); - assert_eq!(next, *test_chars.get(6).unwrap()); - } - - #[test] - fn it_rewinds() { - let mut ctm = CharTapeMachine::new(TEST_STRING.chars().collect()); - let test_chars: Vec = TEST_STRING.chars().collect(); - - ctm.next_char().unwrap(); - ctm.next_char().unwrap(); - assert_eq!(ctm.next_char(), Some(*test_chars.get(3).unwrap())); - - ctm.rewind(1); - assert_eq!(ctm.next_char(), Some(*test_chars.get(2).unwrap())); - } - - #[test] - fn it_seeks() { - let mut ctm = CharTapeMachine::new(TEST_STRING.chars().collect()); - let test_chars: Vec = TEST_STRING.chars().collect(); - - assert_eq!(ctm.next_char(), Some(*test_chars.get(1).unwrap())); - ctm.seek_one().unwrap(); - assert_eq!(ctm.next_char(), Some(*test_chars.get(3).unwrap())); - ctm.seek_one().unwrap(); - ctm.seek_whitespace(); - assert_eq!(ctm.next_char(), Some(*test_chars.get(6).unwrap())); - } - - #[test] - fn it_asserts_chars() -> TapeResult<()> { - let mut ctm = CharTapeMachine::new(TEST_STRING.chars().collect()); - ctm.assert_any(&['A', 'B', 'T'], None)?; - ctm.seek_one().unwrap(); - ctm.assert_char(&'E', None)?; - ctm.seek_one().unwrap(); - ctm.assert_str_sequence("ST ", None)?; - ctm.seek_one().unwrap(); - ctm.assert_any_sequence(&[&['C'], &['A'], &['A', 'B'], &['S', 'T', 'R']], None)?; - - if let Ok(_) = - ctm.assert_any_sequence(&[&['C'], &['A'], &['A', 'B'], &['S', 'T', 'R']], None) - { - Err(TapeError::new(0)) - } else { - Ok(()) - } - } - - #[test] - fn it_checks_eof() -> TapeResult<()> { - let mut ctm = CharTapeMachine::new(TEST_STRING.chars().collect()); - let _ = ctm.get_string_until_any(&['n'], &[]); - assert!(ctm.check_eof()); - - Ok(()) - } -} +pub mod error; +pub mod input; +mod traits; diff --git a/src/tapemachine.rs b/src/tapemachine.rs deleted file mode 100644 index 0868682..0000000 --- a/src/tapemachine.rs +++ /dev/null @@ -1,424 +0,0 @@ -use std::error::Error; -use std::fmt::{self, Display, Formatter}; - -#[derive(Debug)] -pub struct TapeError { - index: usize, -} - -impl TapeError { - pub fn new(index: usize) -> Self { - Self { index } - } - - /// Returns the index the error occured on - pub fn get_index(&self) -> usize { - self.index - } -} - -impl Display for TapeError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "Tape Error at: {}", self.index) - } -} - -impl Error for TapeError {} - -pub type TapeResult = Result; - -const ESCAPE: char = '\\'; - -pub struct CharTapeMachine { - index: usize, - text: Vec, - current_char: char, - previous_char: char, -} - -impl CharTapeMachine { - pub fn new(text: Vec) -> Self { - let current_char = if text.len() > 0 { - *text.first().unwrap() - } else { - ' ' - }; - Self { - text, - index: 0, - previous_char: current_char, - current_char, - } - } - - #[inline] - pub fn get_text(&self) -> Vec { - self.text.clone() - } - - #[inline] - pub fn get_index(&self) -> usize { - self.index - } - - /// returns the current char - #[inline] - pub fn get_current(&self) -> char { - self.current_char - } - - /// Creates an error at the current position - #[inline] - pub fn err(&self) -> TapeError { - TapeError::new(self.index) - } - - /// Returns the next char - /// if there is any - pub fn next_char(&mut self) -> Option { - if self.index < self.text.len() { - self.index += 1; - self.previous_char = self.current_char; - self.current_char = *self.text.get(self.index)?; - - Some(self.current_char) - } else { - None - } - } - - /// Peeks the next available char - #[inline] - pub fn peek_char(&mut self) -> Option { - Some(*self.text.get(self.index + 1)?) - } - - /// Rewinds to a given index - #[inline] - pub fn rewind(&mut self, index: usize) { - if self.text.len() > index { - self.index = index; - self.current_char = *self.text.get(index).unwrap(); - if self.index > 0 { - self.previous_char = *self.text.get(index - 1).unwrap(); - } - } - } - - /// Rewinds to a given index and returns an error - #[inline] - pub fn rewind_with_error(&mut self, index: usize) -> TapeError { - self.rewind(index); - TapeError::new(index) - } - - /// Seeks one character or returns an error - /// if there is no next character - #[inline] - pub fn seek_one(&mut self) -> TapeResult<()> { - if let Some(_) = self.next_char() { - Ok(()) - } else { - Err(TapeError::new(self.index)) - } - } - - /// Seeks one character and returns - /// if it seeked or an error occurred - #[inline] - pub fn try_seek(&mut self) -> bool { - self.seek_one().is_ok() - } - - /// Seeks any character of the given group until none is encountered anymore - pub fn seek_any(&mut self, chars: &[char]) -> TapeResult<()> { - while self.check_any(chars) { - self.seek_one()?; - } - - Ok(()) - } - - /// Seeks until it encounters a non whitespace character - pub fn seek_whitespace(&mut self) { - if self.current_char.is_whitespace() { - while let Some(next) = self.next_char() { - if !next.is_whitespace() || self.check_escaped() { - break; - } - } - } - } - - /// Checks if the machine has reached the eof - pub fn check_eof(&self) -> bool { - self.index >= self.text.len() - } - - /// checks if the current char is escaped - #[inline] - pub fn check_escaped(&mut self) -> bool { - let start = self.index; - - let escaped = if self.previous_char == ESCAPE { - self.rewind(start - 1); - !self.check_escaped() - } else { - false - }; - self.rewind(start); - - escaped - } - - /// Returns true if the given character is equal to the current one - /// and the current character is not escaped - #[inline] - pub fn check_char(&mut self, value: &char) -> bool { - self.current_char == *value && !self.check_escaped() - } - - /// Checks if one of the given chars matches the current one - #[inline] - pub fn check_any(&mut self, chars: &[char]) -> bool { - !self.check_escaped() && chars.contains(&self.current_char) - } - - /// checks if the next characters match a given sequence of characters - pub fn check_sequence(&mut self, sequence: &[char]) -> bool { - let start_index = self.index; - - if self.check_escaped() { - self.rewind(start_index); - - false - } else { - for sq_character in sequence { - if self.current_char != *sq_character { - self.rewind(start_index); - return false; - } - if self.next_char() == None { - self.rewind(start_index); - return false; - } - } - if self.index > 0 { - self.rewind(self.index - 1); - } - true - } - } - - // checks if the next characters mach a string sequence - pub fn check_str_sequence(&mut self, sequence: &str) -> bool { - let start_index = self.index; - - if self.check_escaped() { - self.rewind(start_index); - - false - } else { - let matches = sequence.chars().all(|sq_character| { - if self.current_char != sq_character { - self.rewind(start_index); - return false; - } - if self.next_char() == None { - self.rewind(start_index); - return false; - } - true - }); - if !matches { - false - } else { - if self.index > 0 { - self.rewind(self.index - 1); - } - true - } - } - } - - /// checks if the next characters match any given sequence - #[inline] - pub fn check_any_sequence(&mut self, sequences: &[&[char]]) -> bool { - for seq in sequences { - if self.check_sequence(*seq) { - return true; - } - } - - false - } - - /// checks if the next characters match any given sequence of strings - #[inline] - pub fn check_any_str_sequence(&mut self, sequences: &[&str]) -> bool { - for str_seq in sequences { - if self.check_str_sequence(str_seq) { - return true; - } - } - - false - } - - /// returns an error on the current position and optionally rewinds - /// if a rewind index is given - #[inline] - pub fn assert_error(&mut self, rewind_index: Option) -> TapeError { - if let Some(index) = rewind_index { - self.rewind_with_error(index) - } else { - TapeError::new(self.index) - } - } - - /// returns an error if the given char doesn't match the current one and rewinds - /// if a rewind index is given - #[inline] - pub fn assert_char(&mut self, value: &char, rewind_index: Option) -> TapeResult<()> { - if self.check_char(value) { - Ok(()) - } else { - Err(self.assert_error(rewind_index)) - } - } - - /// returns an error if the current char doesn't match any of the given group - #[inline] - pub fn assert_any(&mut self, chars: &[char], rewind_index: Option) -> TapeResult<()> { - if self.check_any(chars) { - Ok(()) - } else { - Err(self.assert_error(rewind_index)) - } - } - - /// returns an error if the next chars don't match a special sequence - #[inline] - pub fn assert_sequence( - &mut self, - sequence: &[char], - rewind_index: Option, - ) -> TapeResult<()> { - if self.check_sequence(sequence) { - Ok(()) - } else { - Err(self.assert_error(rewind_index)) - } - } - - /// returns an error if the next chars don't match a special sequence - #[inline] - pub fn assert_str_sequence( - &mut self, - sequence: &str, - rewind_index: Option, - ) -> TapeResult<()> { - if self.check_str_sequence(sequence) { - Ok(()) - } else { - Err(self.assert_error(rewind_index)) - } - } - - /// returns an error if the next chars don't match any given sequence - pub fn assert_any_sequence( - &mut self, - sequences: &[&[char]], - rewind_index: Option, - ) -> TapeResult<()> { - if self.check_any_sequence(sequences) { - Ok(()) - } else { - Err(self.assert_error(rewind_index)) - } - } - - /// returns an error if the next chars don't match any given sequence - pub fn assert_any_str_sequence( - &mut self, - sequences: &[&str], - rewind_index: Option, - ) -> TapeResult<()> { - if self.check_any_str_sequence(sequences) { - Ok(()) - } else { - Err(self.assert_error(rewind_index)) - } - } - - /// returns the string until any given character is matched is matched. - /// rewinds with error if it encounters a character form the error group - #[inline] - pub fn get_string_until_any(&mut self, until: &[char], err_at: &[char]) -> TapeResult { - let start_index = self.index; - - self.get_string_until_any_or_rewind(until, err_at, start_index) - } - - /// Returns the string until it encounters a given sequence or rewinds with error - /// if it encounters an err sequence - pub fn get_string_until_sequence( - &mut self, - until: &[&[char]], - err_at: &[&[char]], - ) -> Result { - let start_index = self.index; - let mut result = String::new(); - - if self.check_any_sequence(until) { - return Ok(result); - } else if self.check_any_sequence(err_at) { - return Err(TapeError::new(self.index)); - } - - result.push(self.current_char); - while let Some(ch) = self.next_char() { - if self.check_any_sequence(until) || self.check_any_sequence(err_at) { - break; - } - result.push(ch); - } - - if self.check_any_sequence(err_at) { - Err(self.rewind_with_error(start_index)) - } else { - Ok(result) - } - } - - /// returns the string until a special char is found - /// or rewinds if an err_at char is found - pub fn get_string_until_any_or_rewind( - &mut self, - until: &[char], - err_at: &[char], - rewind_index: usize, - ) -> TapeResult { - let mut result = String::new(); - - if self.check_any(until) { - return Ok(result); - } else if self.check_any(err_at) { - return Err(self.rewind_with_error(rewind_index)); - } - - result.push(self.current_char); - while let Some(ch) = self.next_char() { - if self.check_any(until) || self.check_any(err_at) { - break; - } - result.push(ch); - } - - if self.check_any(err_at) { - Err(self.rewind_with_error(rewind_index)) - } else { - Ok(result) - } - } -} diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000..7294f59 --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,3 @@ +use tokio::io::{AsyncRead, AsyncSeek}; + +pub trait AsyncReadSeek: AsyncRead + AsyncSeek + Unpin {}