1use super::{Ui, theme::SPINNER_TICK_MS};
2use indicatif::{ProgressBar, ProgressStyle};
3use std::io::Write;
4use std::time::Duration;
5
6#[must_use = "progress handles must be held until the operation completes"]
7pub struct ProgressHandle(ProgressBar);
8
9impl ProgressHandle {
10 fn progress(&self) -> &ProgressBar {
11 &self.0
12 }
13
14 pub fn set_length(&self, length: u64) {
15 self.progress().set_length(length);
16 }
17
18 pub fn set_message(&self, message: impl Into<String>) {
19 self.progress().set_message(message.into());
20 }
21
22 pub fn inc(&self, amount: u64) {
23 self.progress().inc(amount);
24 }
25}
26
27impl Drop for ProgressHandle {
28 fn drop(&mut self) {
29 self.0.finish_and_clear();
30 }
31}
32
33#[must_use = "spinner guards must be held until the phase ends"]
34pub struct SpinnerGuard(ProgressBar);
35
36impl SpinnerGuard {
37 pub fn update_message(&self, msg: impl Into<String>) {
38 self.0.set_message(msg.into());
39 }
40}
41
42impl Drop for SpinnerGuard {
43 fn drop(&mut self) {
44 self.0.finish_and_clear();
45 }
46}
47
48impl<W: Write> Ui<W> {
49 pub fn progress_bar(&self) -> ProgressHandle {
50 let pb = ProgressBar::new(0);
51 pb.set_style(ProgressStyle::clone(&self.progress_style));
52 ProgressHandle(pb)
53 }
54
55 pub fn start_spinner(&self, message: impl Into<String>) -> SpinnerGuard {
56 let spinner = ProgressBar::new_spinner();
57 spinner.set_style(ProgressStyle::clone(&self.spinner_style));
58 spinner.set_message(message.into());
59 spinner.enable_steady_tick(Duration::from_millis(SPINNER_TICK_MS));
60 SpinnerGuard(spinner)
61 }
62
63 pub fn spinner<T, F: FnOnce() -> T>(&self, message: impl Into<String>, f: F) -> T {
64 let _guard = self.start_spinner(message);
65 f()
66 }
67}