parent
b0532856d5
commit
3df49ca9a9
@ -1,50 +0,0 @@
|
|||||||
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<Box<dyn AsyncReadSeek>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InputReader {
|
|
||||||
pub fn new<T: AsyncReadSeek + 'static>(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<char> {
|
|
||||||
self.read_next().await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the next char without forwarding
|
|
||||||
pub async fn peek(&mut self) -> TapeResult<char> {
|
|
||||||
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<bool> {
|
|
||||||
let char = self.read_next().await?;
|
|
||||||
|
|
||||||
Ok(char == '\x00')
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads the next char returning \x00 for EOF
|
|
||||||
async fn read_next(&mut self) -> TapeResult<char> {
|
|
||||||
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()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,81 @@
|
|||||||
|
use crate::error::{TapeError, TapeResult};
|
||||||
|
use std::io::ErrorKind;
|
||||||
|
use tokio::io::{AsyncBufRead, AsyncBufReadExt};
|
||||||
|
|
||||||
|
/// An Input reader to asynchronously read a type
|
||||||
|
/// that implements AsyncBufRead and AsyncSeek.
|
||||||
|
pub struct InputReader {
|
||||||
|
inner: Box<dyn AsyncBufRead + Unpin>,
|
||||||
|
buf: String,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputReader {
|
||||||
|
pub fn new<T: AsyncBufRead + Unpin + 'static>(inner: T) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Box::new(inner),
|
||||||
|
buf: String::new(),
|
||||||
|
index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the next char consuming it in the process
|
||||||
|
#[inline]
|
||||||
|
pub async fn consume(&mut self) -> TapeResult<char> {
|
||||||
|
self.read_next().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the next char without forwarding
|
||||||
|
#[inline]
|
||||||
|
pub async fn peek(&mut self) -> TapeResult<char> {
|
||||||
|
let char = self.read_next().await?;
|
||||||
|
self.seek_to(self.index - 1).await?;
|
||||||
|
|
||||||
|
Ok(char)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns if EOF has been reached
|
||||||
|
#[inline]
|
||||||
|
pub async fn check_eof(&mut self) -> bool {
|
||||||
|
if let Err(TapeError::EOF) = self.read_next().await {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the next char returning \x00 for EOF
|
||||||
|
async fn read_next(&mut self) -> TapeResult<char> {
|
||||||
|
self.seek_to(self.index + 1).await?;
|
||||||
|
let result = self
|
||||||
|
.buf
|
||||||
|
.get(self.index - 1..self.index)
|
||||||
|
.ok_or(TapeError::EOF)?
|
||||||
|
.chars()
|
||||||
|
.next()
|
||||||
|
.ok_or(TapeError::EOF);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Seeks to a given index
|
||||||
|
pub async fn seek_to(&mut self, to_index: usize) -> TapeResult<()> {
|
||||||
|
while to_index >= self.buf.len() {
|
||||||
|
let mut line = String::new();
|
||||||
|
self.inner.read_line(&mut line).await.map_err(|e| {
|
||||||
|
if e.kind() == ErrorKind::UnexpectedEof {
|
||||||
|
TapeError::EOF
|
||||||
|
} else {
|
||||||
|
TapeError::TokioIoError(e)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
if line.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.buf.push_str(&line);
|
||||||
|
}
|
||||||
|
self.index = to_index;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod input;
|
pub mod input_reader;
|
||||||
mod traits;
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
mod test_input;
|
@ -0,0 +1,41 @@
|
|||||||
|
use crate::error::{TapeError, TapeResult};
|
||||||
|
use crate::input_reader::InputReader;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
fn get_reader() -> InputReader {
|
||||||
|
let data = "ABCDEFG HIJKLMNOP 12345567890\nSecond Line\n\n";
|
||||||
|
InputReader::new(Cursor::new(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn it_peeks() {
|
||||||
|
let mut reader = get_reader();
|
||||||
|
assert_eq!(reader.peek().await.unwrap(), 'A');
|
||||||
|
assert_eq!(reader.peek().await.unwrap(), 'A');
|
||||||
|
assert_eq!(reader.peek().await.unwrap(), 'A');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn it_consumes() {
|
||||||
|
let mut reader = get_reader();
|
||||||
|
assert_eq!(reader.consume().await.unwrap(), 'A');
|
||||||
|
assert_eq!(reader.consume().await.unwrap(), 'B');
|
||||||
|
assert_eq!(reader.consume().await.unwrap(), 'C');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn it_checks_for_eof() {
|
||||||
|
let mut reader = get_reader();
|
||||||
|
assert!(!is_eof(reader.seek_to(29).await));
|
||||||
|
assert!(!reader.check_eof().await);
|
||||||
|
assert!(!is_eof(reader.seek_to(47).await));
|
||||||
|
assert!(is_eof(reader.consume().await.map(|_| ())));
|
||||||
|
assert!(reader.check_eof().await);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_eof(result: TapeResult<()>) -> bool {
|
||||||
|
match result {
|
||||||
|
Err(TapeError::EOF) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +0,0 @@
|
|||||||
use tokio::io::{AsyncRead, AsyncSeek};
|
|
||||||
|
|
||||||
pub trait AsyncReadSeek: AsyncRead + AsyncSeek + Unpin {}
|
|
Loading…
Reference in New Issue