#![deny(missing_copy_implementations, missing_debug_implementations, missing_docs, trivial_casts,
trivial_numeric_casts, unsafe_code, unused_extern_crates, unused_import_braces,
unused_qualifications, unused_results, variant_size_differences, warnings)]
#![cfg_attr(feature = "cargo-clippy", deny(clippy))]
#[macro_use]
extern crate failure;
use failure::{Backtrace, Context, Fail, ResultExt};
use std::fmt::{self, Display, Formatter};
use std::io::{self, BufRead, Read};
use std::result;
use std::str::{self, FromStr};
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
pub enum ErrorKind {
#[fail(display = "IO error")]
Io,
#[fail(display = "Input data is not utf8")]
Utf8,
#[fail(display = "Could not parse string as type")]
Parse,
}
#[derive(Debug)]
pub struct Error {
inner: Context<ErrorKind>,
}
impl Error {
pub fn kind(&self) -> ErrorKind {
*self.inner.get_context()
}
}
impl Fail for Error {
fn cause(&self) -> Option<&Fail> {
self.inner.cause()
}
fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace()
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.inner, f)
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}
}
impl From<Context<ErrorKind>> for Error {
fn from(inner: Context<ErrorKind>) -> Error {
Error { inner }
}
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug)]
pub struct InputStream<T: BufRead> {
reader: T,
byte_buffer: Vec<u8>,
}
fn is_whitespace(c: u8) -> bool {
match c {
b' ' | b'\x09'...b'\x0d' => true,
_ => false,
}
}
fn act_while<T, F, G>(reader: &mut T, mut condition: F, mut act: G) -> io::Result<()>
where
T: BufRead,
F: FnMut(&&u8) -> bool,
G: FnMut(&[u8]),
{
loop {
let (skipped, done) = match reader.fill_buf() {
Ok(buf) => {
let skipped = buf.iter().take_while(&mut condition).count();
act(&buf[..skipped]);
(skipped, skipped < buf.len() || buf.is_empty())
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
};
reader.consume(skipped);
if done {
break;
}
}
Ok(())
}
impl<T: BufRead> InputStream<T> {
pub fn new(reader: T) -> InputStream<T> {
InputStream {
reader,
byte_buffer: Vec::new(),
}
}
pub fn scan<F>(&mut self) -> Result<F>
where
F: FromStr,
<F as FromStr>::Err: Fail,
{
let &mut InputStream {
ref mut reader,
ref mut byte_buffer,
} = self;
act_while(reader, |&&c| is_whitespace(c), |_| {}).context(ErrorKind::Io)?;
byte_buffer.clear();
act_while(
reader,
|&&c| !is_whitespace(c),
|slice| byte_buffer.extend_from_slice(slice),
).context(ErrorKind::Io)?;
let slice = match byte_buffer.split_last() {
Some((&b' ', slice)) => slice,
_ => byte_buffer.as_slice(),
};
Ok(str::from_utf8(slice)
.context(ErrorKind::Utf8)?
.parse::<F>()
.context(ErrorKind::Parse)?)
}
}
impl<T: BufRead> Read for InputStream<T> {
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
self.reader.read(buffer)
}
}
impl<T: BufRead> BufRead for InputStream<T> {
fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
self.reader.fill_buf()
}
fn consume(&mut self, amount: usize) {
self.reader.consume(amount)
}
}
#[cfg(test)]
mod tests {
use super::*;
const EPS: f32 = 1e-6;
#[test]
fn simple_strings() {
let text = "Howdy neighbour, how are you doing?";
let mut stream = InputStream::new(text.as_bytes());
let first: String = stream.scan().expect("First string");
let second: String = stream.scan().expect("Second string");
let third: String = stream.scan().expect("Third string");
assert_eq!(first, "Howdy");
assert_eq!(second, "neighbour,");
assert_eq!(third, "how");
}
#[test]
fn simple_numbers() {
let text = "5 -7 12.5 -2.85";
let mut stream = InputStream::new(text.as_bytes());
assert_eq!(5, stream.scan().expect("5"));
assert_eq!(-7, stream.scan().expect("-7"));
assert_eq!(
true,
(12.5 - stream.scan::<f32>().expect("12.5")).abs() < EPS
);
assert_eq!(
true,
(-2.85 - stream.scan::<f32>().expect("-2.85")).abs() < EPS
);
}
#[test]
fn newlines() {
let text = "12\nHello";
let mut stream = InputStream::new(text.as_bytes());
assert_eq!(12, stream.scan().expect("12"));
assert_eq!("Hello", stream.scan::<String>().expect("Hello"));
}
#[test]
fn test_non_utf8() {
let text: [u8; 1] = [255];
let mut stream = InputStream::new(&text[..]);
assert_eq!(true, stream.scan::<i32>().is_err());
}
#[test]
fn test_not_parsing() {
let text = "hello";
let mut stream = InputStream::new(text.as_bytes());
assert_eq!(true, stream.scan::<i32>().is_err());
}
}