1use console::{Key, Term};
2use std::sync::atomic::{AtomicUsize, Ordering};
3use std::sync::mpsc::{self, Receiver, Sender};
4use std::sync::{Arc, Mutex};
5use std::thread::{self, JoinHandle};
6use std::time::Duration;
7
8#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
10pub struct Ref(usize);
11
12impl Ref {
13 pub fn new() -> Self {
16 static COUNTER: AtomicUsize = AtomicUsize::new(1);
17 Self(COUNTER.fetch_add(1, Ordering::Relaxed))
18 }
19}
20
21impl Default for Ref {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl std::fmt::Display for Ref {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 write!(f, "#Ref<{}>", self.0)
30 }
31}
32
33pub trait Progress: Send + Sync {
37 fn append(&mut self, msg: &str) -> Ref;
40 fn clear_prompt(&mut self);
42 fn failed(&mut self, references: Ref);
44 fn hide(&mut self, reference: Ref);
46 fn println(&mut self, reference: Ref, msg: &str);
49 fn print_inline(&mut self, msg: &str);
51 fn prompt(&mut self, msg: &str);
54 fn render(&mut self);
57 fn set_message(&mut self, reference: Ref, msg: String);
59 fn set_prompt_input(&mut self, input: String);
62 fn show(&mut self, reference: Ref);
64 fn succeeded(&mut self, reference: Ref);
66}
67
68enum ProgressMessage {
69 Tick,
70 Shutdown,
71}
72
73pub struct ProgressBar {
74 progress: Arc<Mutex<Box<dyn Progress>>>,
75 renderer: Arc<Mutex<Option<JoinHandle<()>>>>,
76 sender: Arc<Mutex<Sender<ProgressMessage>>>,
77 ticker: Arc<Mutex<Option<JoinHandle<()>>>>,
78 counter: *mut Counter,
79}
80
81struct Counter(AtomicUsize);
82
83unsafe impl std::marker::Send for ProgressBar {}
84unsafe impl std::marker::Sync for ProgressBar {}
85
86impl ProgressBar {
87 fn counter(&self) -> &Counter {
88 unsafe { &*self.counter }
89 }
90
91 pub fn new(bar: Box<dyn Progress>) -> Self {
92 let _ = Term::stdout().hide_cursor();
93 let _ = Term::stderr().hide_cursor();
94
95 let (sender, receiver) = mpsc::channel();
96 let counter = Box::into_raw(Box::new(Counter(AtomicUsize::new(1))));
97 let progress = Arc::new(Mutex::new(bar));
98
99 let progress_bar = Self {
100 progress: progress.clone(),
101 renderer: Arc::new(Mutex::new(None)),
102 sender: Arc::new(Mutex::new(sender.clone())),
103 ticker: Arc::new(Mutex::new(None)),
104 counter,
105 };
106
107 let renderer = thread::spawn(move || {
108 Self::start_renderer(progress, receiver);
109 });
110
111 let ticker_sender = sender.clone();
112 let ticker = thread::spawn(move || loop {
113 thread::sleep(Duration::from_millis(80));
114 if ticker_sender.send(ProgressMessage::Tick).is_err() {
115 break;
116 }
117 });
118
119 {
120 let mut render_handle = progress_bar.renderer.lock().unwrap();
121 *render_handle = Some(renderer);
122 }
123 {
124 let mut ticker_handle = progress_bar.ticker.lock().unwrap();
125 *ticker_handle = Some(ticker);
126 }
127
128 progress_bar
129 }
130
131 pub fn append(&mut self, msg: &str) -> Ref {
132 self.progress.lock().unwrap().append(msg)
133 }
134 pub fn failed(&mut self, reference: Ref) {
135 self.progress.lock().unwrap().failed(reference)
136 }
137 pub fn hide(&mut self, reference: Ref) {
138 self.progress.lock().unwrap().hide(reference)
139 }
140 pub fn println(&mut self, reference: Ref, msg: &str) {
141 self.progress.lock().unwrap().println(reference, msg)
142 }
143 pub fn print_inline(&mut self, msg: &str) {
144 let mut progress = self.progress.lock().unwrap();
145 progress.print_inline(msg);
146 progress.render();
147 }
148 pub fn prompt(&mut self, msg: &str) -> String {
149 self.progress.lock().unwrap().prompt(msg);
150 let input = self.read_input();
151 self.progress.lock().unwrap().clear_prompt();
152 input.trim().into()
153 }
154 pub fn set_message(&mut self, reference: Ref, msg: String) {
155 self.progress.lock().unwrap().set_message(reference, msg)
156 }
157 pub fn show(&mut self, reference: Ref) {
158 self.progress.lock().unwrap().show(reference)
159 }
160 pub fn succeeded(&mut self, reference: Ref) {
161 self.progress.lock().unwrap().succeeded(reference)
162 }
163
164 fn start_renderer(
165 progress: Arc<Mutex<Box<dyn Progress>>>,
166 receiver: Receiver<ProgressMessage>,
167 ) {
168 while let Ok(message) = receiver.recv() {
169 match message {
170 ProgressMessage::Tick => progress.lock().unwrap().render(),
171 ProgressMessage::Shutdown => {
172 break;
173 }
174 }
175 }
176 }
177
178 fn read_input(&mut self) -> String {
179 let term = Term::stdout();
180 let mut input = String::new();
181
182 loop {
183 match term.read_key().unwrap() {
184 Key::Enter => break,
185 Key::Char(c) => {
186 input.push(c);
187 }
188 Key::Backspace => {
189 if !input.is_empty() {
190 input.pop();
191 }
192 }
193 _ => {}
194 }
195 self.progress
196 .lock()
197 .unwrap()
198 .set_prompt_input(input.clone());
199 }
200 input
201 }
202}
203
204impl Clone for ProgressBar {
205 fn clone(&self) -> Self {
206 self.counter().0.fetch_add(1, Ordering::Relaxed);
207 Self {
208 progress: Arc::clone(&self.progress),
209 renderer: Arc::clone(&self.renderer),
210 sender: Arc::clone(&self.sender),
211 ticker: Arc::clone(&self.ticker),
212 counter: self.counter,
213 }
214 }
215}
216
217impl Drop for ProgressBar {
218 fn drop(&mut self) {
219 let counter = self.counter().0.fetch_sub(1, Ordering::AcqRel);
220 if counter == 1 {
221 let _ = self.sender.lock().unwrap().send(ProgressMessage::Shutdown);
222
223 let mut join_handle = self.ticker.lock().unwrap();
224 if let Some(ticker) = join_handle.take() {
225 let _ = ticker.join();
226 }
227
228 let mut join_handle = self.renderer.lock().unwrap();
229 if let Some(renderer) = join_handle.take() {
230 let _ = renderer.join();
231 }
232
233 let _ = Term::stdout().show_cursor();
234 let _ = Term::stderr().show_cursor();
235 }
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242 use std::sync::{Arc, Mutex};
243
244 #[derive(Default)]
245 struct MockProgress {
246 appended: Arc<Mutex<Vec<(Ref, String)>>>,
247 failed_refs: Arc<Mutex<Vec<Ref>>>,
248 hidden_refs: Arc<Mutex<Vec<Ref>>>,
249 println_calls: Arc<Mutex<Vec<(Ref, String)>>>,
250 prompt_calls: Arc<Mutex<Vec<String>>>,
251 set_message_calls: Arc<Mutex<Vec<(Ref, String)>>>,
252 shown_refs: Arc<Mutex<Vec<Ref>>>,
253 inline_calls: Arc<Mutex<Vec<String>>>,
254 succeeded_refs: Arc<Mutex<Vec<Ref>>>,
255 }
256
257 impl MockProgress {
258 fn new() -> Self {
259 Self::default()
260 }
261 }
262
263 impl Progress for MockProgress {
264 fn append(&mut self, msg: &str) -> Ref {
265 let reference = Ref::new();
266 self.appended
267 .lock()
268 .unwrap()
269 .push((reference, msg.to_string()));
270 reference
271 }
272
273 fn clear_prompt(&mut self) {}
274
275 fn failed(&mut self, reference: Ref) {
276 self.failed_refs.lock().unwrap().push(reference);
277 }
278
279 fn hide(&mut self, reference: Ref) {
280 self.hidden_refs.lock().unwrap().push(reference);
281 }
282
283 fn println(&mut self, reference: Ref, msg: &str) {
284 self.println_calls
285 .lock()
286 .unwrap()
287 .push((reference, msg.to_string()));
288 }
289
290 fn print_inline(&mut self, msg: &str) {
291 self.inline_calls.lock().unwrap().push(msg.to_string());
292 }
293
294 fn prompt(&mut self, msg: &str) {
295 self.prompt_calls.lock().unwrap().push(msg.to_string());
296 }
297
298 fn render(&mut self) {}
299
300 fn set_message(&mut self, reference: Ref, msg: String) {
301 self.set_message_calls
302 .lock()
303 .unwrap()
304 .push((reference, msg));
305 }
306
307 fn set_prompt_input(&mut self, _input: String) {}
308
309 fn show(&mut self, reference: Ref) {
310 self.shown_refs.lock().unwrap().push(reference);
311 }
312
313 fn succeeded(&mut self, reference: Ref) {
314 self.succeeded_refs.lock().unwrap().push(reference);
315 }
316 }
317
318 #[test]
319 fn ref_new_generates_unique_ids() {
320 let ref1 = Ref::new();
321 let ref2 = Ref::new();
322 let ref3 = Ref::new();
323
324 assert_ne!(ref1, ref2);
325 assert_ne!(ref2, ref3);
326 assert_ne!(ref1, ref3);
327 }
328
329 #[test]
330 fn ref_default_creates_new_ref() {
331 let ref1 = Ref::default();
332 let ref2 = Ref::default();
333
334 assert_ne!(ref1, ref2);
335 }
336
337 #[test]
338 fn ref_is_copy_and_clone() {
339 let ref1 = Ref::new();
340 let ref2 = ref1; let ref3 = ref1; assert_eq!(ref1, ref2);
344 assert_eq!(ref1, ref3);
345 assert_eq!(ref2, ref3);
346 }
347
348 #[test]
349 fn progress_bar_append_delegates_to_implementation() {
350 let mock = MockProgress::new();
351 let appended_calls = Arc::clone(&mock.appended);
352 let mut progress_bar = ProgressBar::new(Box::new(mock));
353
354 let ref1 = progress_bar.append("test message 1");
355 let ref2 = progress_bar.append("test message 2");
356
357 let calls = appended_calls.lock().unwrap();
358 assert_eq!(
359 *calls,
360 vec![
361 (ref1, "test message 1".to_string()),
362 (ref2, "test message 2".to_string())
363 ]
364 );
365 }
366
367 #[test]
368 fn progress_bar_failed_delegates_to_implementation() {
369 let mock = MockProgress::new();
370 let failed_calls = Arc::clone(&mock.failed_refs);
371 let mut progress_bar = ProgressBar::new(Box::new(mock));
372
373 let reference = Ref::new();
374 progress_bar.failed(reference);
375
376 let calls = failed_calls.lock().unwrap();
377 assert_eq!(*calls, vec![reference]);
378 }
379
380 #[test]
381 fn progress_bar_succeeded_delegates_to_implementation() {
382 let mock = MockProgress::new();
383 let succeeded_calls = Arc::clone(&mock.succeeded_refs);
384 let mut progress_bar = ProgressBar::new(Box::new(mock));
385
386 let reference = Ref::new();
387 progress_bar.succeeded(reference);
388
389 let calls = succeeded_calls.lock().unwrap();
390 assert_eq!(*calls, vec![reference]);
391 }
392
393 #[test]
394 fn progress_bar_hide_delegates_to_implementation() {
395 let mock = MockProgress::new();
396 let hidden_calls = Arc::clone(&mock.hidden_refs);
397 let mut progress_bar = ProgressBar::new(Box::new(mock));
398
399 let reference = Ref::new();
400 progress_bar.hide(reference);
401
402 let calls = hidden_calls.lock().unwrap();
403 assert_eq!(*calls, vec![reference]);
404 }
405
406 #[test]
407 fn progress_bar_show_delegates_to_implementation() {
408 let mock = MockProgress::new();
409 let shown_calls = Arc::clone(&mock.shown_refs);
410 let mut progress_bar = ProgressBar::new(Box::new(mock));
411
412 let reference = Ref::new();
413 progress_bar.show(reference);
414
415 let calls = shown_calls.lock().unwrap();
416 assert_eq!(*calls, vec![reference]);
417 }
418
419 #[test]
420 fn progress_bar_println_delegates_to_implementation() {
421 let mock = MockProgress::new();
422 let println_calls = Arc::clone(&mock.println_calls);
423 let mut progress_bar = ProgressBar::new(Box::new(mock));
424
425 let reference = Ref::new();
426 progress_bar.println(reference, "test output");
427
428 let calls = println_calls.lock().unwrap();
429 assert_eq!(*calls, vec![(reference, "test output".to_string())]);
430 }
431
432 #[test]
433 fn progress_bar_set_message_delegates_to_implementation() {
434 let mock = MockProgress::new();
435 let set_message_calls = Arc::clone(&mock.set_message_calls);
436 let mut progress_bar = ProgressBar::new(Box::new(mock));
437
438 let reference = Ref::new();
439 progress_bar.set_message(reference, "updated message".to_string());
440
441 let calls = set_message_calls.lock().unwrap();
442 assert_eq!(*calls, vec![(reference, "updated message".to_string())]);
443 }
444
445 #[test]
446 fn progress_bar_is_send_and_sync() {
447 fn assert_send<T: Send>() {}
448 fn assert_sync<T: Sync>() {}
449
450 assert_send::<ProgressBar>();
451 assert_sync::<ProgressBar>();
452 }
453
454 #[test]
455 fn progress_bar_clone() {
456 let mock = MockProgress::new();
457 let progress_bar = ProgressBar::new(Box::new(mock));
458 let cloned_progress_bar = progress_bar.clone();
459
460 drop(progress_bar);
461 drop(cloned_progress_bar);
462 }
463}