input_stream/
lib.rs

1//! A small utility library for input parsing in a manner akin to _C++_'s istream.
2//!
3//! It exposes a single struct [`InputStream`](struct.InputStream.html) which is wrapped around
4//! any object that implements
5//! [`std::io::BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html).
6//!
7//! It can parse any type which implements
8//! [`std::str::FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html).
9//!
10//! # Usage
11//!
12//! This crate is [on crates.io](https://crates.io/crates/input-stream) and can be used
13//! by adding `input-stream` to the dependencies in your project's `Cargo.toml`:
14//!
15//! ```toml
16//! [dependencies]
17//! input-stream = "0.3.0"
18//! ```
19//!
20//! and this in your crate root:
21//!
22//! ```rust
23//! extern crate input_stream;
24//! ```
25//!
26//! # Examples:
27//!
28//! ```rust
29//! use std::io;
30//! use input_stream::InputStream;
31//!
32//! # let buf_reader = "test".as_bytes();
33//! # use std::io::BufRead;
34//! let mut input = InputStream::new(buf_reader);
35//! let value = input.scan::<bool>();
36//! match value {
37//!     Ok(value) => println!("Successfully read boolean: {}", value),
38//!     Err(err) => println!("Error reading value: {:?}", err)
39//! }
40//! ```
41//!
42//! ## Reading from standard input:
43//!
44//! ```rust,no_run
45//! use std::io;
46//! use input_stream::InputStream;
47//!
48//! let stdin = io::stdin();
49//! let mut input = InputStream::new(stdin.lock());
50//!
51//! let integer: i32 = input.scan().expect("An integer");
52//! let string: String = input.scan().expect("A string");
53//!
54//! println!("Read the number: {} and the string {}", integer, string);
55//! ```
56//!
57//! ## or from a file
58//!
59//! ```rust,no_run
60//! use std::io::{self, BufReader};
61//! use std::fs::File;
62//! use input_stream::InputStream;
63//!
64//! let mut input = InputStream::new(
65//!     BufReader::new(File::open("name_of_file.txt").expect("a file named name_of_file.txt")));
66//!
67//! let value: f32 = input.scan().expect("A floating point number");
68//!
69//! println!("Read a float: {}", value);
70//!
71
72#![deny(
73    missing_copy_implementations,
74    missing_debug_implementations,
75    missing_docs,
76    trivial_casts,
77    trivial_numeric_casts,
78    unsafe_code,
79    unused_extern_crates,
80    unused_import_braces,
81    unused_qualifications,
82    unused_results,
83    variant_size_differences,
84    clippy::all,
85    warnings
86)]
87
88use std::fmt::{self, Debug, Display, Formatter};
89use std::io::{self, BufRead, Read};
90use std::str::{self, FromStr};
91
92/// The type of errors this library can return.
93#[derive(Debug)]
94pub enum Error<E> {
95    /// I/O Error
96    Io(io::Error),
97    /// Data is not valid utf8
98    Utf8(str::Utf8Error),
99    /// Could not parse given data type
100    FromStr(E),
101    /// Buffer limit exceeded
102    BufferLimitExceeded,
103}
104
105/// A specialized [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html) for this
106/// library's errors.
107pub type Result<T, E = Error<<T as FromStr>::Err>> = std::result::Result<T, E>;
108
109impl<E> From<io::Error> for Error<E> {
110    fn from(err: io::Error) -> Self {
111        Error::Io(err)
112    }
113}
114
115impl<E> From<str::Utf8Error> for Error<E> {
116    fn from(err: str::Utf8Error) -> Self {
117        Error::Utf8(err)
118    }
119}
120
121impl<E> Display for Error<E> {
122    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
123        match self {
124            Error::Io(_) => write!(fmt, "I/O Error"),
125            Error::Utf8(_) => write!(fmt, "Data is not valid utf8"),
126            Error::FromStr(_) => write!(fmt, "Could not parse given data type"),
127            Error::BufferLimitExceeded => write!(fmt, "Buffer limit exceeded"),
128        }
129    }
130}
131
132impl<E: Debug> std::error::Error for Error<E> {}
133
134/// A wrapper for [`std::io::BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html).
135///
136/// To get an instance of this  struct use static method [`new`](struct.InputStream.html#method.new) on
137/// `InputStream`.
138#[derive(Debug)]
139pub struct InputStream<T: BufRead> {
140    reader: T,
141    byte_buffer: Vec<u8>,
142}
143
144#[inline(always)]
145fn is_whitespace(c: u8) -> bool {
146    match c {
147        b' ' | b'\x09'...b'\x0d' => true,
148        _ => false,
149    }
150}
151
152#[inline(always)]
153fn act_while<T, F, G, E>(reader: &mut T, mut condition: F, mut act: G) -> Result<(), Error<E>>
154where
155    T: BufRead,
156    F: FnMut(&&u8) -> bool,
157    G: FnMut(&[u8]) -> Result<(), Error<E>>,
158{
159    loop {
160        let (skipped, done) = match reader.fill_buf() {
161            Ok(buf) => {
162                let skipped = buf.iter().take_while(&mut condition).count();
163                act(&buf[..skipped])?;
164                (skipped, skipped < buf.len() || buf.is_empty())
165            }
166            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
167            Err(e) => return Err(e.into()),
168        };
169
170        reader.consume(skipped);
171        if done {
172            break;
173        }
174    }
175    Ok(())
176}
177
178impl<T: BufRead> InputStream<T> {
179    /// Creates an instance of InputStream which wraps the given
180    /// [`std::io::BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html).
181    #[inline(always)]
182    pub fn new(reader: T) -> InputStream<T> {
183        InputStream {
184            reader,
185            byte_buffer: Vec::new(),
186        }
187    }
188
189    /// Scan the underlying buffered reader for a value of a type that implements
190    /// [`std::str::FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html)
191    /// returning a [`Result`](type.Result.html).
192    ///
193    /// An example on how to use scan is at the [`crate documentation`](index.html).
194    pub fn scan<F: FromStr>(&mut self) -> Result<F> {
195        self.inner_scan(None)
196    }
197
198    /// Scan the underlying buffer reader for a value of a type that implements
199    /// [`std::str::FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html)
200    /// returning a [`Result`](type.Result.html).
201    ///
202    /// This is a refined version of [`scan`](struct.InputStream.html#method.scan) which allows
203    /// limits to be placed on the maximum size of the internal buffer
204    pub fn scan_with_limit<F: FromStr>(&mut self, limit: usize) -> Result<F> {
205        self.inner_scan(Some(limit))
206    }
207
208    #[inline(always)]
209    fn inner_scan<F: FromStr>(&mut self, limit: Option<usize>) -> Result<F> {
210        let &mut InputStream {
211            ref mut reader,
212            ref mut byte_buffer,
213        } = self;
214        act_while(reader, |&&c| is_whitespace(c), |_| Ok(()))?;
215        byte_buffer.clear();
216        act_while(
217            reader,
218            |&&c| !is_whitespace(c),
219            |slice| {
220                if let Some(limit) = limit {
221                    if byte_buffer.len() + slice.len() > limit {
222                        return Err(Error::BufferLimitExceeded);
223                    }
224                }
225
226                byte_buffer.extend_from_slice(slice);
227                Ok(())
228            },
229        )?;
230
231        let slice = match byte_buffer.split_last() {
232            Some((&b' ', slice)) => slice,
233            _ => byte_buffer.as_slice(),
234        };
235
236        str::from_utf8(slice)?.parse().map_err(Error::FromStr)
237    }
238}
239
240impl<T: BufRead> Read for InputStream<T> {
241    #[inline(always)]
242    fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
243        self.reader.read(buffer)
244    }
245}
246
247impl<T: BufRead> BufRead for InputStream<T> {
248    #[inline(always)]
249    fn fill_buf(&mut self) -> io::Result<&[u8]> {
250        self.reader.fill_buf()
251    }
252
253    #[inline(always)]
254    fn consume(&mut self, amount: usize) {
255        self.reader.consume(amount)
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262    const EPS: f32 = 1e-6;
263
264    #[test]
265    fn simple_strings() {
266        let text = "Howdy neighbour, how are you doing?";
267        let mut stream = InputStream::new(text.as_bytes());
268
269        let first: String = stream.scan().expect("First string");
270        let second: String = stream.scan().expect("Second string");
271        let third: String = stream.scan().expect("Third string");
272        assert_eq!(first, "Howdy");
273        assert_eq!(second, "neighbour,");
274        assert_eq!(third, "how");
275    }
276
277    #[test]
278    fn simple_numbers() {
279        let text = "5 -7 12.5 -2.85";
280        let mut stream = InputStream::new(text.as_bytes());
281        assert_eq!(5, stream.scan().expect("5"));
282        assert_eq!(-7, stream.scan().expect("-7"));
283        assert_eq!(
284            true,
285            (12.5 - stream.scan::<f32>().expect("12.5")).abs() < EPS
286        );
287        assert_eq!(
288            true,
289            (-2.85 - stream.scan::<f32>().expect("-2.85")).abs() < EPS
290        );
291    }
292
293    #[test]
294    fn newlines() {
295        let text = "12\nHello";
296        let mut stream = InputStream::new(text.as_bytes());
297        assert_eq!(12, stream.scan().expect("12"));
298        assert_eq!("Hello", stream.scan::<String>().expect("Hello"));
299    }
300
301    #[test]
302    fn test_non_utf8() {
303        let text: [u8; 1] = [255];
304        let mut stream = InputStream::new(&text[..]);
305        assert_eq!(true, stream.scan::<i32>().is_err());
306    }
307
308    #[test]
309    fn test_not_parsing() {
310        let text = "hello";
311        let mut stream = InputStream::new(text.as_bytes());
312        assert_eq!(true, stream.scan::<i32>().is_err());
313    }
314
315    #[test]
316    fn test_limit_buffer() {
317        let text = "25 150 -250";
318        let mut stream = InputStream::new(text.as_bytes());
319        assert_eq!(25, stream.scan_with_limit(3).expect("25"));
320        assert_eq!(150, stream.scan_with_limit(3).expect("150"));
321        assert!(stream.scan_with_limit::<i32>(3).is_err());
322    }
323}