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