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
use std::fmt::Arguments; use std::io::{Result, Write}; use std::time::Instant; use bytesize::ByteSize; pub struct Progress<W> { inner: W, ts: Instant, pos: u64, len: u64, clear: bool, buffer: Vec<u8>, } impl<W: Write> Progress<W> { pub fn new(inner: W, len: u64) -> Self { Self { inner, ts: Instant::now(), pos: 0, len, clear: false, buffer: Vec::with_capacity(256), } } #[inline] pub fn pos(&mut self, pos: u64) { self.pos = pos; } pub fn render(&mut self) -> Result<()> { self.buffer.clear(); let (width, _) = termion::terminal_size()?; let elapsed = self.ts.elapsed().as_secs(); let bytes_per_second = if elapsed > 0 { ByteSize(self.pos / elapsed) } else { ByteSize(0) }; let percentage = self.pos as f32 / self.len as f32; if self.clear { write!(self.buffer, "{}", termion::cursor::Restore)?; } write!(self.buffer, "{}", termion::cursor::Save)?; let bar_width = width - 45; let bar_filled = (bar_width as f32 * percentage).ceil() as u16; for _ in 0..bar_filled { write!(self.buffer, "█")?; } for _ in bar_filled..bar_width { write!(self.buffer, "░")?; } write!( self.buffer, " {}/{} ({:.2}%) - {}", ByteSize(self.pos), ByteSize(self.len), percentage * 100.0, bytes_per_second, )?; self.inner.write_all(&self.buffer)?; self.flush()?; self.clear = true; Ok(()) } #[inline] pub fn finish(self) -> W { self.inner } } impl<W: Write> Write for Progress<W> { #[inline] fn write(&mut self, buf: &[u8]) -> Result<usize> { self.inner.write(buf) } #[inline] fn flush(&mut self) -> Result<()> { self.inner.flush() } #[inline] fn write_fmt(&mut self, fmt: Arguments) -> Result<()> { if self.clear { write!( self.inner, "{}{}", termion::clear::CurrentLine, termion::cursor::Restore )?; self.clear = false; } self.inner.write_fmt(fmt) } }