use std::io::{self, Write};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProgressBar {
total: u64,
position: u64,
width: usize,
filled_char: char,
empty_char: char,
label: Option<String>,
show_percentage: bool,
}
impl ProgressBar {
pub fn new(total: u64) -> Self {
ProgressBar {
total: total.max(1),
position: 0,
width: 40,
filled_char: '█',
empty_char: '░',
label: None,
show_percentage: true,
}
}
pub fn with_width(mut self, width: usize) -> Self {
self.width = width;
self
}
pub fn with_chars(mut self, filled: char, empty: char) -> Self {
self.filled_char = filled;
self.empty_char = empty;
self
}
pub fn with_label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
pub fn show_percentage(mut self, show: bool) -> Self {
self.show_percentage = show;
self
}
pub fn set_position(&mut self, position: u64) {
self.position = position.min(self.total);
}
pub fn inc(&mut self, delta: u64) {
self.set_position(self.position + delta);
}
pub fn ratio(&self) -> f64 {
self.position as f64 / self.total as f64
}
pub fn draw(&self) {
let ratio = self.ratio();
let filled_len = ((self.width as f64) * ratio).round() as usize;
let filled_len = filled_len.min(self.width);
let mut line = String::new();
line.push('\r');
if let Some(label) = &self.label {
line.push_str(label);
line.push(' ');
}
line.push('[');
for _ in 0..filled_len {
line.push(self.filled_char);
}
for _ in filled_len..self.width {
line.push(self.empty_char);
}
line.push(']');
if self.show_percentage {
line.push_str(&format!(" {:>3.0}%", ratio * 100.0));
}
print!("{}", line);
let _ = io::stdout().flush();
}
pub fn finish(&mut self) {
self.position = self.total;
self.draw();
println!();
}
pub fn finish_with_message(&mut self, message: impl AsRef<str>) {
self.position = self.total;
self.draw();
println!(" {}", message.as_ref());
}
}