use crate::utils::Color;
use std::default::Default;
use std::io::{Stdout, Write};
use std::sync::{Arc, Mutex};
pub struct ProgressBar {
pub start: u64,
pub goal: u64,
pub get_progress: Arc<Mutex<dyn Fn() -> u64 + Send>>,
pub style: Style,
}
pub struct Style {
pub bar_character: char,
pub bar_length: u64,
pub color: Color,
}
impl Default for Style {
fn default() -> Self {
Style {
bar_character: '=',
bar_length: 50,
color: Color::White,
}
}
}
pub struct StyleBuilder {
bar_character: Option<char>,
bar_length: Option<u64>,
color: Option<Color>,
}
impl StyleBuilder {
pub fn new() -> StyleBuilder {
StyleBuilder {
bar_character: None,
bar_length: None,
color: None,
}
}
pub fn bar_character(mut self, character: char) -> StyleBuilder {
self.bar_character = Some(character);
self
}
pub fn bar_length(mut self, length: u64) -> StyleBuilder {
self.bar_length = Some(length);
self
}
pub fn color(mut self, color: Color) -> StyleBuilder {
self.color = Some(color);
self
}
pub fn build(self) -> Style {
Style {
bar_character: self.bar_character.unwrap_or('='),
bar_length: self.bar_length.unwrap_or(50),
color: self.color.unwrap_or(Color::White),
}
}
}
impl ProgressBar {
pub fn new<F>(start: u64, goal: u64, get_progress: F, style: Style) -> ProgressBar
where
F: Fn() -> u64 + Send + 'static,
{
assert!(start <= goal);
ProgressBar {
start,
goal,
get_progress: Arc::new(Mutex::new(get_progress)),
style,
}
}
pub fn start(&self, writer: &mut Stdout) {
let mut current_value = self.start;
println!("\x1B[?25l"); while current_value < self.goal {
current_value = (self.get_progress.lock().unwrap())();
self.update_display(writer, current_value);
}
write!(writer, "\n\x1b[0m").unwrap(); }
fn update_display(&self, writer: &mut dyn Write, current_value: u64) {
let percentage =
((current_value - self.start) as f64 / (self.goal - self.start) as f64) * 100.0;
let bar_length = self.style.bar_length as usize;
let completed = ((percentage / 100.0) * bar_length as f64) as usize;
let bar = self.style.bar_character.to_string().repeat(completed)
+ &" ".repeat(bar_length - completed);
let color_code = self.style.color.to_ansi_code();
write!(writer, "\r{}[{}]", &color_code, bar).unwrap();
writer.flush().unwrap();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_update_progress_success() {
let progress_bar = ProgressBar::new(0, 100, || 0, Style::default());
let mut writer = Cursor::new(Vec::new());
progress_bar.update_display(&mut writer, 50);
let expected_output = "\r\x1b[37m[========================= ]"; assert_eq!(writer.get_ref().as_slice(), expected_output.as_bytes());
}
}