diff --git a/Cargo.toml b/Cargo.toml index 7fd123b..230c6a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ repository = "https://github.com/Trivernis/charred-rs" [dependencies] thiserror = "1.0.24" log = "0.4.14" +async-trait = "0.1.50" [dependencies.tokio] version = "1.5.0" diff --git a/src/input_reader.rs b/src/input_reader.rs index 824cb1e..65d820c 100644 --- a/src/input_reader.rs +++ b/src/input_reader.rs @@ -5,13 +5,13 @@ use tokio::io::{AsyncBufRead, AsyncBufReadExt}; /// An Input reader to asynchronously read a type /// that implements AsyncBufRead and AsyncSeek. pub struct InputReader { - inner: Box, + inner: Box, buf: String, index: usize, } impl InputReader { - pub fn new(inner: T) -> Self { + pub fn new(inner: T) -> Self { Self { inner: Box::new(inner), buf: String::new(), @@ -37,7 +37,7 @@ impl InputReader { /// Returns if EOF has been reached #[inline] pub async fn check_eof(&mut self) -> bool { - if let Err(TapeError::EOF) = self.read_next().await { + if let Err(TapeError::EOF) = self.peek().await { true } else { false diff --git a/src/lib.rs b/src/lib.rs index 40bc48b..d06b43c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub mod error; pub mod input_reader; +pub mod token; #[cfg(test)] mod tests; diff --git a/src/tests/mod.rs b/src/tests/mod.rs index b993b10..124aebd 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1 +1,2 @@ mod test_input; +mod test_token; diff --git a/src/tests/test_token.rs b/src/tests/test_token.rs new file mode 100644 index 0000000..ba474e2 --- /dev/null +++ b/src/tests/test_token.rs @@ -0,0 +1,56 @@ +use crate::error::TapeResult; +use crate::input_reader::InputReader; +use crate::token::{ProtoToken, Token}; +use async_trait::async_trait; +use std::io::Cursor; + +#[derive(Debug)] +struct TestToken(i32); + +#[async_trait] +impl ProtoToken for TestToken { + async fn try_parse(reader: &mut InputReader) -> TapeResult> { + let mut num = String::new(); + while !reader.check_eof().await && reader.peek().await?.is_numeric() { + num.push(reader.consume().await?); + } + if num.is_empty() { + Ok(None) + } else { + Ok(Some(Token::new(TestToken(num.parse::().unwrap())))) + } + } +} + +#[tokio::test] +async fn it_parses() { + let mut reader = InputReader::new(Cursor::new("128")); + let token = TestToken::try_parse(&mut reader).await.unwrap(); + assert!(token.is_some()); + let token = token.unwrap().try_into::().unwrap(); + assert_eq!(token.0, 128); + + let mut reader = InputReader::new(Cursor::new("string a12 24\n")); + let token = TestToken::try_parse(&mut reader).await.unwrap(); + assert!(token.is_none()); + reader.seek_to(8).await.unwrap(); + + let token = TestToken::try_parse(&mut reader).await.unwrap(); + assert!(token.is_some()); + let token = token.unwrap().try_into::().unwrap(); + assert_eq!(token.0, 12); +} + +#[test] +fn it_converts() { + let token = Token::new(TestToken(12)); + assert!(token.is::()); + + let test_token = token.try_as::(); + assert!(test_token.is_some()); + assert_eq!(test_token.unwrap().0, 12); + + let test_token = token.try_into::(); + assert!(test_token.is_some()); + assert_eq!(test_token.unwrap().0, 12); +} diff --git a/src/token.rs b/src/token.rs new file mode 100644 index 0000000..ccc57e3 --- /dev/null +++ b/src/token.rs @@ -0,0 +1,40 @@ +use crate::error::TapeResult; +use crate::input_reader::InputReader; +use async_trait::async_trait; +use std::any::{Any, TypeId}; + +#[async_trait] +pub trait ProtoToken { + /// Tries parsing the token + async fn try_parse(reader: &mut InputReader) -> TapeResult>; +} + +pub struct Token { + inner: Box, +} + +impl Token { + /// Constructs a new token + pub fn new(inner: A) -> Self { + Self { + inner: Box::new(inner), + } + } + + /// Tries downcasting the value to a concrete type + pub fn try_as(&self) -> Option<&T> { + self.inner.downcast_ref::() + } + + pub fn try_into(self) -> Option { + match self.inner.downcast() { + Ok(value) => Some(*value), + Err(_) => None, + } + } + + /// Checks if the inner value is of a given concrete type + pub fn is(&self) -> bool { + self.inner.as_ref().type_id() == TypeId::of::() + } +}