use std::cell::RefCell;
use std::rc::Rc;
use std::time::{Duration, Instant};
use term_size::dimensions_stderr;
use crate::cli::pad_truncate;
const EIGHT: [char; 9] = [
' ',
'\u{258f}', '\u{258e}', '\u{258d}', '\u{258c}',
'\u{258b}', '\u{258a}', '\u{2589}', '\u{2588}'
];
pub struct ProgressBar {
width: usize,
amount: f64, buffer: String,
}
impl ProgressBar {
pub fn new(width: usize) -> ProgressBar {
ProgressBar { width, amount: 0f64, buffer: String::new() }
}
pub fn update(&mut self, amount: f64) -> &str {
self.amount = amount;
self.render()
}
pub fn render(&mut self) -> &str {
let mut units = (self.amount * self.width as f64 * 8f64).floor() as isize;
self.buffer.truncate(0);
for _i in 0..self.width {
self.buffer.push(EIGHT[units.clamp(0, 8) as usize]);
units -= 8;
}
&self.buffer
}
}
const BAR_WIDTH: usize = 32;
pub struct ProgressLine {
bar: ProgressBar,
start_time: Instant,
last_update: Instant,
pub debounce: Duration,
width: usize,
message: String,
pub show_bar: bool,
pub show_ever: bool,
completion: f64,
}
impl ProgressLine {
pub fn new() -> ProgressLine {
let bar = ProgressBar::new(BAR_WIDTH);
let start_time = Instant::now();
let last_update = start_time - Duration::from_secs(3600);
let debounce = Duration::from_millis(100);
let width = dimensions_stderr().map(|(cols, _rows)| cols - 1).unwrap_or(79);
let message = String::default();
let show_bar = true;
let show_ever = true;
let completion = 0f64;
ProgressLine { bar, start_time, last_update, debounce, width, message, show_bar, show_ever, completion }
}
pub fn to_shared(self) -> Rc<RefCell<ProgressLine>> {
Rc::new(RefCell::new(self))
}
pub fn update(&mut self, completion: f64, message: String) -> &mut Self {
self.completion = completion;
self.message = message;
self
}
pub fn complete(&mut self, completion: f64) -> &mut Self {
self.completion = completion;
self
}
pub fn force_update(&mut self) -> &mut Self {
self.last_update -= self.debounce;
self
}
pub fn display(&mut self) {
if !self.show_ever { return; }
let now = Instant::now();
if now.duration_since(self.last_update) < self.debounce {
return;
}
let duration = now.duration_since(self.start_time);
let seconds = duration.as_secs();
let minutes = seconds / 60;
let hours = minutes / 60;
let time = format!("{:02}:{:02}:{:02}", hours, minutes % 60, seconds % 60);
let bar = if self.show_bar {
format!("\u{2507}{}\u{2507} {:>3}% ",
self.bar.update(self.completion),
(self.completion * 100f64).floor() as usize
)
} else {
String::new()
};
let width = 11 + if self.show_bar { BAR_WIDTH + 8 } else { 0 };
eprint!("\r[{}] {}{}", time, bar, pad_truncate(&self.message, self.width - width));
self.last_update = now;
}
pub fn clear(&mut self) {
if self.show_ever {
eprint!("\r{}\r", pad_truncate("", self.width));
}
self.force_update();
}
}
impl Default for ProgressLine {
fn default() -> Self {
ProgressLine::new()
}
}