1use std::fs::File;
19use std::io::BufReader;
20use std::io::Read;
21use std::path::PathBuf;
22use std::time::{Duration, Instant};
23
24pub trait ReadWithSize: Read {
25 fn total_read(&self) -> usize;
30
31 fn assummed_total_size(&self) -> usize;
33
34 fn fraction(&self) -> f64;
37
38 fn read_start_time(&self) -> Option<Instant>;
41
42 fn eta(&self) -> Option<Duration> {
45 self.read_start_time().map(|read_start_time| {
46 let duration_since_start = Instant::now() - read_start_time;
47 duration_since_start.div_f64(self.fraction()) - duration_since_start
48 })
49 }
50
51 fn etc(&self) -> Option<Instant> {
54 self.read_start_time().map(|read_start_time| {
55 let duration_since_start = Instant::now() - read_start_time;
56 read_start_time + duration_since_start.div_f64(self.fraction())
57 })
58 }
59
60 fn est_total_time(&self) -> Option<Duration> {
63 self.read_start_time().map(|read_start_time| {
64 let duration_since_start = Instant::now() - read_start_time;
65 duration_since_start.div_f64(self.fraction())
66 })
67 }
68
69 fn bytes_per_sec(&self) -> Option<f64> {
72 self.read_start_time().map(|read_start_time| {
73 let since_start = Instant::now() - read_start_time;
74 (self.total_read() as f64) / since_start.as_secs_f64()
75 })
76 }
77}
78
79pub struct ReaderWithSize<R: Read> {
81 inner: R,
82
83 total_size: usize,
84 total_read: usize,
85 read_start_time: Option<Instant>,
86}
87
88impl<R: Read> ReaderWithSize<R> {
89 pub fn new(total_size: usize, inner: R) -> Self {
91 ReaderWithSize {
92 total_size,
93 total_read: 0,
94 inner,
95 read_start_time: None,
96 }
97 }
98
99 pub fn into_inner(self) -> R {
101 self.inner
102 }
103
104 pub fn inner(&self) -> &R {
106 &self.inner
107 }
108
109 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
110 let result = self.inner.read(buf);
111 if let Ok(bytes_read) = result {
112 self.total_read += bytes_read;
113 }
114 if self.read_start_time.is_none() {
115 self.read_start_time = Some(Instant::now());
116 }
117 result
118 }
119}
120
121impl<R: Read> ReadWithSize for ReaderWithSize<R> {
122 fn total_read(&self) -> usize {
124 self.total_read
125 }
126
127 fn assummed_total_size(&self) -> usize {
129 self.total_size
130 }
131
132 fn fraction(&self) -> f64 {
135 (self.total_read as f64) / (self.total_size as f64)
136 }
137
138 fn read_start_time(&self) -> Option<Instant> {
141 self.read_start_time
142 }
143}
144
145impl<R: Read> Read for ReaderWithSize<R> {
146 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
147 self.read(buf)
148 }
149}
150
151impl ReaderWithSize<File> {
152 pub fn from_path(path: impl Into<PathBuf>) -> Result<Self, std::io::Error> {
154 let path: PathBuf = path.into();
155
156 let file = File::open(path)?;
157 ReaderWithSize::from_file(file)
158 }
159
160 pub fn from_file(file: File) -> Result<Self, std::io::Error> {
162 let size = file.metadata()?.len() as usize;
163
164 Ok(Self::new(size, file))
165 }
166}
167
168pub struct BufReaderWithSize<R: Read>(BufReader<ReaderWithSize<R>>);
169
170impl BufReaderWithSize<File> {
171 pub fn from_path(path: impl Into<PathBuf>) -> Result<Self, std::io::Error> {
173 let path: PathBuf = path.into();
174
175 let file = File::open(path)?;
176
177 BufReaderWithSize::from_file(file)
178 }
179
180 pub fn from_file(file: File) -> Result<Self, std::io::Error> {
182 let size = file.metadata()?.len() as usize;
183
184 let rdr = ReaderWithSize::new(size, file);
185 let rdr = BufReader::new(rdr);
186
187 Ok(BufReaderWithSize(rdr))
188 }
189}
190
191impl<R: Read> Read for BufReaderWithSize<R> {
192 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
193 self.0.read(buf)
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200 use std::io::Cursor;
201 use std::thread::sleep;
202
203 #[test]
204 fn basic() {
205 let bytes = "hello".as_bytes();
206 let mut reader = ReaderWithSize::new(5, Cursor::new(bytes));
207 assert_eq!(reader.assummed_total_size(), 5);
208
209 let mut buf = vec![0];
210
211 reader.read_exact(&mut buf).unwrap();
212 assert_eq!(buf, vec!['h' as u8]);
213 assert_eq!(reader.total_read(), 1);
214 assert_eq!(reader.fraction(), 0.2);
215
216 let mut buf = vec![0, 0];
217 reader.read_exact(&mut buf).unwrap();
218 assert_eq!(buf, vec!['e' as u8, 'l' as u8]);
219 assert_eq!(reader.total_read(), 3);
220 assert_eq!(reader.fraction(), 0.6);
221
222 let _cursor: &Cursor<&[u8]> = reader.inner();
223 let _cursor: Cursor<&[u8]> = reader.into_inner();
224 }
225
226 #[test]
227 fn eta1() {
228 let start = Instant::now();
229 let bytes = "hello".as_bytes();
230 let mut reader = ReaderWithSize::new(5, Cursor::new(bytes));
231
232 assert_eq!(reader.eta(), None);
234
235 let mut buf = vec![0];
236 reader.read_exact(&mut buf).unwrap();
237
238 sleep(Duration::from_millis(10));
240
241 let eta = reader.eta();
244 let bytes_per_sec = reader.bytes_per_sec();
245 let etc = reader.etc();
246
247 assert!(eta.is_some());
248 let eta: Duration = eta.unwrap();
249
250 assert!(eta >= Duration::from_millis(40));
251 assert!(40. / 1000. - eta.as_secs_f64() <= 1.);
252
253 assert!(bytes_per_sec.is_some());
254 let bytes_per_sec: f64 = bytes_per_sec.unwrap();
255 assert!(bytes_per_sec >= 20.); assert!(bytes_per_sec < 100.);
257
258 assert!(etc.is_some());
259 let etc: Instant = etc.unwrap();
260 assert!(etc > start);
261 assert!(etc < start + Duration::from_secs(1));
262 }
263}