1use std::{
9 fs::File,
10 io::{self, BufRead, BufReader, Seek, SeekFrom},
11 path::Path,
12};
13
14use crate::file::FileReader;
15
16pub struct TextReader {
18 file: BufReader<File>,
19 buf: String,
20 count: u64,
21}
22
23pub struct Iter<'a> {
24 reader: &'a mut TextReader,
25}
26
27pub struct IntoIter {
28 reader: TextReader,
29}
30
31impl FileReader for TextReader {
32 fn from_path<P>(path: P) -> io::Result<Self>
33 where
34 Self: Sized,
35 P: AsRef<Path>,
36 {
37 TextReader::from_file(File::open(path)?)
38 }
39
40 fn from_file(file: File) -> io::Result<Self>
41 where
42 Self: Sized,
43 {
44 let reader = TextReader {
45 file: BufReader::new(file),
46 buf: String::new(),
47 count: 0,
48 };
49
50 Ok(reader)
51 }
52
53 #[inline]
54 fn size(&self) -> u64 {
55 let metadata = self
56 .file
57 .get_ref()
58 .metadata()
59 .expect("Could not get text file's size");
60
61 metadata.len()
62 }
63}
64
65impl TextReader {
66 #[inline]
89 pub fn read_line(&mut self) -> io::Result<String> {
90 self.buf.clear();
91
92 self.file
93 .read_line(&mut self.buf)
94 .and_then(|buf_size| {
95 if buf_size == 0 {
96 return Err(io::Error::new(
97 io::ErrorKind::UnexpectedEof,
98 "The end of the file has been reached",
99 ));
100 }
101
102 self.count += 1;
103
104 if self.buf.ends_with('\n') {
105 self.buf.pop();
106
107 if self.buf.ends_with('\r') {
108 self.buf.pop();
109 }
110 }
111
112 Ok(self.buf.clone())
113 })
114 }
115
116 #[inline]
136 pub fn iter(&mut self) -> Iter<'_> {
137 Iter {
138 reader: self
139 }
140 }
141}
142
143impl Seek for TextReader {
144 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
145 self.file.seek(pos)
146 }
147}
148
149impl Iterator for Iter<'_> {
150 type Item = String;
151
152 fn next(&mut self) -> Option<Self::Item> {
153 match self.reader.read_line() {
154 Ok(line) => Some(line),
155 Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => None,
156
157 Err(_) => panic!(
158 "An error occurred on line {} when reading text file",
159 self.reader.count + 1,
160 ),
161 }
162 }
163}
164
165impl IntoIterator for TextReader {
166 type Item = String;
167 type IntoIter = IntoIter;
168
169 fn into_iter(self) -> Self::IntoIter {
170 IntoIter {
171 reader: self
172 }
173 }
174}
175
176impl Iterator for IntoIter {
177 type Item = String;
178
179 fn next(&mut self) -> Option<Self::Item> {
180 match self.reader.read_line() {
181 Ok(line) => Some(line),
182 Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => None,
183
184 Err(_) => panic!(
185 "An error occurred on line {} when reading text file",
186 self.reader.count + 1,
187 ),
188 }
189 }
190}