1use indicatif::{ProgressBar, ProgressStyle};
4use std::time::Duration;
5
6pub struct ProgressTracker {
8 bar: ProgressBar,
9}
10
11impl ProgressTracker {
12 pub fn new(total_size: u64, operation: &str) -> Self {
14 let bar = ProgressBar::new(total_size);
15 bar.set_style(
16 ProgressStyle::default_bar()
17 .template(&format!(
18 "{} [{{elapsed_precise}}] [{{bar:40.cyan/blue}}] {{bytes}}/{{total_bytes}} ({{eta}})",
19 operation
20 ))
21 .unwrap()
22 .progress_chars("█▉▊▋▌▍▎▏ "),
23 );
24 bar.enable_steady_tick(Duration::from_millis(100));
25
26 Self { bar }
27 }
28
29 pub fn new_spinner(operation: &str) -> Self {
31 let bar = ProgressBar::new_spinner();
32 bar.set_style(
33 ProgressStyle::default_spinner()
34 .template(&format!("{} [{{elapsed_precise}}] {{spinner}} {{msg}}", operation))
35 .unwrap(),
36 );
37 bar.enable_steady_tick(Duration::from_millis(100));
38
39 Self { bar }
40 }
41
42 pub fn inc(&self, bytes: u64) {
44 self.bar.inc(bytes);
45 }
46
47 pub fn set_position(&self, pos: u64) {
49 self.bar.set_position(pos);
50 }
51
52 pub fn set_message(&self, msg: &str) {
54 self.bar.set_message(msg.to_string());
55 }
56
57 pub fn finish(&self, message: &str) {
59 self.bar.finish_with_message(message.to_string());
60 }
61
62 pub fn finish_and_clear(&self) {
64 self.bar.finish_and_clear();
65 }
66}
67
68impl Drop for ProgressTracker {
69 fn drop(&mut self) {
70 self.bar.finish_and_clear();
71 }
72}
73
74pub type ProgressCallback = Box<dyn Fn(u64, u64) + Send + Sync>;
76
77pub fn create_progress_callback(tracker: &ProgressTracker) -> impl Fn(u64) + '_ {
79 move |bytes_processed| {
80 tracker.inc(bytes_processed);
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use std::thread;
88 use std::time::Duration;
89
90 #[test]
91 fn test_progress_tracker() {
92 let tracker = ProgressTracker::new(1000, "Testing");
93
94 for _i in 0..10 {
96 tracker.inc(100);
97 thread::sleep(Duration::from_millis(10));
98 }
99
100 tracker.finish("Complete");
101 }
102
103 #[test]
104 fn test_spinner() {
105 let tracker = ProgressTracker::new_spinner("Processing");
106
107 tracker.set_message("Working...");
108 thread::sleep(Duration::from_millis(50));
109
110 tracker.set_message("Almost done...");
111 thread::sleep(Duration::from_millis(50));
112
113 tracker.finish("Done");
114 }
115}