1use crate::utils::Color;
2use std::default::Default;
3use std::io::{Stdout, Write};
4use std::sync::{Arc, Mutex};
5
6pub struct ProgressBar {
8 pub start: u64,
13
14 pub goal: u64,
16
17 pub get_progress: Arc<Mutex<dyn Fn() -> u64 + Send>>,
19
20 pub style: Style,
21}
22
23pub struct Style {
25 pub bar_character: char,
27
28 pub bar_length: u64,
30
31 pub color: Color,
33}
34
35impl Default for Style {
36 fn default() -> Self {
37 Style {
38 bar_character: '=',
39 bar_length: 50,
40 color: Color::White,
41 }
42 }
43}
44
45pub struct StyleBuilder {
46 bar_character: Option<char>,
47 bar_length: Option<u64>,
48 color: Option<Color>,
49}
50
51impl StyleBuilder {
52 pub fn new() -> StyleBuilder {
53 StyleBuilder {
54 bar_character: None,
55 bar_length: None,
56 color: None,
57 }
58 }
59
60 pub fn bar_character(mut self, character: char) -> StyleBuilder {
61 self.bar_character = Some(character);
62 self
63 }
64
65 pub fn bar_length(mut self, length: u64) -> StyleBuilder {
66 self.bar_length = Some(length);
67 self
68 }
69
70 pub fn color(mut self, color: Color) -> StyleBuilder {
71 self.color = Some(color);
72 self
73 }
74
75 pub fn build(self) -> Style {
76 Style {
77 bar_character: self.bar_character.unwrap_or('='),
78 bar_length: self.bar_length.unwrap_or(50),
79 color: self.color.unwrap_or(Color::White),
80 }
81 }
82}
83
84impl ProgressBar {
85 pub fn new<F>(start: u64, goal: u64, get_progress: F, style: Style) -> ProgressBar
87 where
88 F: Fn() -> u64 + Send + 'static,
89 {
90 assert!(start <= goal);
91
92 ProgressBar {
93 start,
94 goal,
95 get_progress: Arc::new(Mutex::new(get_progress)),
96 style,
97 }
98 }
99
100 pub fn start(&self, writer: &mut Stdout) {
103 let mut current_value = self.start;
104
105 println!("\x1B[?25l"); while current_value < self.goal {
107 current_value = (self.get_progress.lock().unwrap())();
108 self.update_display(writer, current_value);
109 }
110
111 write!(writer, "\n\x1b[0m").unwrap(); }
113
114 fn update_display(&self, writer: &mut dyn Write, current_value: u64) {
117 let percentage =
118 ((current_value - self.start) as f64 / (self.goal - self.start) as f64) * 100.0;
119
120 let bar_length = self.style.bar_length as usize;
121 let completed = ((percentage / 100.0) * bar_length as f64) as usize;
122 let bar = self.style.bar_character.to_string().repeat(completed)
123 + &" ".repeat(bar_length - completed);
124
125 let color_code = self.style.color.to_ansi_code();
126
127 write!(writer, "\r{}[{}]", &color_code, bar).unwrap();
128
129 writer.flush().unwrap();
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use std::io::Cursor;
137
138 #[test]
139 fn test_update_progress_success() {
140 let progress_bar = ProgressBar::new(0, 100, || 0, Style::default());
141 let mut writer = Cursor::new(Vec::new());
142
143 progress_bar.update_display(&mut writer, 50);
144
145 let expected_output = "\r\x1b[37m[========================= ]"; assert_eq!(writer.get_ref().as_slice(), expected_output.as_bytes());
147 }
148}