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
10const DEFAULT_BUFFER_SIZE: usize = 1024;
12
13pub struct BufReader {
15 reader: IoBufReader<File>,
17 buffer: Rc<Vec<u8>>,
19}
20
21impl BufReader {
22 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 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
64pub 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#[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}