1#![allow(dead_code)]
4
5use indicatif::{ProgressBar, ProgressStyle};
6use std::time::Duration;
7
8pub const LARGE_FILE_THRESHOLD: u64 = 10 * 1024 * 1024; pub fn file_progress_bar(total_bytes: u64, operation: &str) -> ProgressBar {
28 use std::io::IsTerminal;
30 let is_tty = std::io::stdout().is_terminal();
31 if total_bytes < LARGE_FILE_THRESHOLD || !is_tty {
32 return ProgressBar::hidden();
33 }
34
35 let pb = ProgressBar::new(total_bytes);
36 pb.set_style(
37 ProgressStyle::default_bar()
38 .template(&format!(
39 "{{spinner:.green}} {} [{{elapsed_precise}}] [{{bar:40.cyan/blue}}] {{bytes}}/{{total_bytes}} ({{bytes_per_sec}}, {{eta}})",
40 operation
41 ))
42 .expect("valid progress template")
43 .progress_chars("=>-"),
44 );
45 pb.enable_steady_tick(Duration::from_millis(100));
46 pb
47}
48
49pub fn file_progress(total: u64, message: &str) -> ProgressBar {
51 let pb = ProgressBar::new(total);
52 pb.set_style(
53 ProgressStyle::default_bar()
54 .template("{spinner:.green} {msg} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec})")
55 .expect("valid template")
56 .progress_chars("=>-"),
57 );
58 pb.set_message(message.to_string());
59 pb.enable_steady_tick(Duration::from_millis(100));
60 pb
61}
62
63pub fn block_progress(total: u64, message: &str) -> ProgressBar {
65 let pb = ProgressBar::new(total);
66 pb.set_style(
67 ProgressStyle::default_bar()
68 .template("{spinner:.green} {msg} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} blocks")
69 .expect("valid template")
70 .progress_chars("=>-"),
71 );
72 pb.set_message(message.to_string());
73 pb.enable_steady_tick(Duration::from_millis(100));
74 pb
75}
76
77pub fn spinner(message: &str) -> ProgressBar {
79 let pb = ProgressBar::new_spinner();
80 pb.set_style(
81 ProgressStyle::default_spinner()
82 .template("{spinner:.green} {msg} [{elapsed_precise}]")
83 .expect("valid template"),
84 );
85 pb.set_message(message.to_string());
86 pb.enable_steady_tick(Duration::from_millis(100));
87 pb
88}
89
90pub fn download_progress(total: u64, filename: &str) -> ProgressBar {
92 let pb = ProgressBar::new(total);
93 pb.set_style(
94 ProgressStyle::default_bar()
95 .template("{spinner:.green} Downloading {msg} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})")
96 .expect("valid template")
97 .progress_chars("=>-"),
98 );
99 pb.set_message(filename.to_string());
100 pb.enable_steady_tick(Duration::from_millis(100));
101 pb
102}
103
104pub fn upload_progress(total: u64, filename: &str) -> ProgressBar {
106 let pb = ProgressBar::new(total);
107 pb.set_style(
108 ProgressStyle::default_bar()
109 .template("{spinner:.green} Uploading {msg} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})")
110 .expect("valid template")
111 .progress_chars("=>-"),
112 );
113 pb.set_message(filename.to_string());
114 pb.enable_steady_tick(Duration::from_millis(100));
115 pb
116}
117
118pub fn batch_progress(total: u64, message: &str) -> ProgressBar {
120 let pb = ProgressBar::new(total);
121 pb.set_style(
122 ProgressStyle::default_bar()
123 .template("{spinner:.green} {msg} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({percent}%)")
124 .expect("valid template")
125 .progress_chars("=>-"),
126 );
127 pb.set_message(message.to_string());
128 pb.enable_steady_tick(Duration::from_millis(100));
129 pb
130}
131
132pub fn finish_success(pb: &ProgressBar, message: &str) {
134 pb.set_style(
135 ProgressStyle::default_bar()
136 .template("{msg}")
137 .expect("valid template"),
138 );
139 pb.finish_with_message(format!("\x1b[32m✓\x1b[0m {}", message));
140}
141
142pub fn finish_error(pb: &ProgressBar, message: &str) {
144 pb.set_style(
145 ProgressStyle::default_bar()
146 .template("{msg}")
147 .expect("valid template"),
148 );
149 pb.finish_with_message(format!("\x1b[31m✗\x1b[0m {}", message));
150}
151
152pub fn finish_spinner_success(pb: &ProgressBar, message: &str) {
154 pb.finish_with_message(format!("\x1b[32m✓\x1b[0m {}", message));
155}
156
157pub fn finish_spinner_error(pb: &ProgressBar, message: &str) {
159 pb.finish_with_message(format!("\x1b[31m✗\x1b[0m {}", message));
160}
161
162pub struct StreamProgress {
164 pb: ProgressBar,
165 total: u64,
166 current: u64,
167}
168
169impl StreamProgress {
170 pub fn new(total: u64, message: &str) -> Self {
172 let pb = file_progress(total, message);
173 Self {
174 pb,
175 total,
176 current: 0,
177 }
178 }
179
180 pub fn update(&mut self, bytes: u64) {
182 self.current += bytes;
183 self.pb.set_position(self.current);
184 }
185
186 pub fn set_position(&mut self, pos: u64) {
188 self.current = pos;
189 self.pb.set_position(pos);
190 }
191
192 pub fn percentage(&self) -> f64 {
194 if self.total == 0 {
195 100.0
196 } else {
197 (self.current as f64 / self.total as f64) * 100.0
198 }
199 }
200
201 pub fn finish(self, message: &str) {
203 finish_success(&self.pb, message);
204 }
205
206 pub fn finish_error(self, message: &str) {
208 finish_error(&self.pb, message);
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_spinner_creation() {
218 let pb = spinner("Testing...");
219 pb.finish_with_message("Done");
220 }
221
222 #[test]
223 fn test_progress_creation() {
224 let pb = file_progress(1000, "Processing");
225 pb.set_position(500);
226 pb.finish_with_message("Complete");
227 }
228
229 #[test]
230 fn test_stream_progress() {
231 let mut sp = StreamProgress::new(100, "Streaming");
232 sp.update(25);
233 assert_eq!(sp.percentage(), 25.0);
234 sp.update(25);
235 assert_eq!(sp.percentage(), 50.0);
236 sp.finish("Done");
237 }
238}