parseit/
reader.rs

1use std::fs::File;
2use std::io::{
3    BufRead, BufReader as IoBufReader, Error as IoError, ErrorKind as IoErrorKind,
4    Result as IoResult,
5};
6use std::path::Path;
7use std::rc::Rc;
8use std::str;
9
10/// Default buffer size of the reader.
11const DEFAULT_BUFFER_SIZE: usize = 1024;
12
13/// Buffered reader.
14pub struct BufReader {
15    /// Inner type.
16    reader: IoBufReader<File>,
17    /// Buffer.
18    buffer: Rc<Vec<u8>>,
19}
20
21impl BufReader {
22    /// Opens the given file and initializes the buffered reader with given buffer size.
23    pub fn open<P: AsRef<Path>>(path: P, buffer_size: Option<usize>) -> IoResult<Self> {
24        let file = File::open(path)?;
25        let reader = IoBufReader::new(file);
26        let buffer = Self::new_buffer(buffer_size);
27        Ok(Self { reader, buffer })
28    }
29
30    /// Creates a new buffer with the given size.
31    fn new_buffer(buffer_size: Option<usize>) -> Rc<Vec<u8>> {
32        Rc::new(Vec::with_capacity(
33            buffer_size.unwrap_or(DEFAULT_BUFFER_SIZE),
34        ))
35    }
36}
37
38impl Iterator for BufReader {
39    type Item = IoResult<Rc<Vec<u8>>>;
40    fn next(&mut self) -> Option<Self::Item> {
41        let buffer = match Rc::get_mut(&mut self.buffer) {
42            Some(rc_buffer) => {
43                rc_buffer.clear();
44                rc_buffer
45            }
46            None => {
47                self.buffer = Self::new_buffer(None);
48                Rc::make_mut(&mut self.buffer)
49            }
50        };
51        self.reader
52            .read_until(b'\n', buffer)
53            .map(|u| {
54                if u == 0 {
55                    None
56                } else {
57                    Some(Rc::clone(&self.buffer))
58                }
59            })
60            .transpose()
61    }
62}
63
64/// Reads the contents of the file into a string.
65///
66/// Uses [`BufReader`] under the hood.
67pub fn read_to_string<P: AsRef<Path>>(path: P) -> IoResult<String> {
68    let mut lines = Vec::<String>::new();
69    for line in BufReader::open(path, None)? {
70        lines.push(
71            str::from_utf8(&line?)
72                .map_err(|e| IoError::new(IoErrorKind::Other, e))?
73                .to_string(),
74        );
75    }
76    Ok(lines.join(""))
77}
78
79/// Reads (decodes) the given gzip file into a string.
80///
81/// Uses [`BufReader`] under the hood.
82#[cfg(feature = "gzip")]
83pub fn read_gzip<P: AsRef<Path>>(path: P) -> IoResult<String> {
84    use std::io::Read;
85    let mut bytes = Vec::<u8>::new();
86    for read_bytes in BufReader::open(path, None)? {
87        bytes.extend(read_bytes?.to_vec());
88    }
89    let mut gz = flate2::read::GzDecoder::new(&bytes[..]);
90    let mut data = String::new();
91    gz.read_to_string(&mut data)?;
92    Ok(data)
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98    use crate::error::Error;
99    use std::path::PathBuf;
100
101    #[test]
102    fn test_file_reader() -> Result<(), Error> {
103        let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml");
104        assert!(read_to_string(path)?.contains(&format!("name = \"{}\"", env!("CARGO_PKG_NAME"))));
105        Ok(())
106    }
107
108    #[cfg(feature = "gzip")]
109    #[test]
110    fn test_gzip_reader() -> Result<(), Error> {
111        use flate2::write::GzEncoder;
112        use flate2::Compression;
113        use std::fs::{self, File};
114        use std::io::Write;
115
116        let data = "gzip-test";
117        let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test.gz");
118        let file = File::create(&path)?;
119        let mut encoder = GzEncoder::new(file, Compression::default());
120        encoder.write_all(data.as_bytes())?;
121        encoder.finish()?;
122
123        assert_eq!(data, read_gzip(&path)?);
124        fs::remove_file(path)?;
125        Ok(())
126    }
127}