Skip to main content

harmont_cli/output/
spinner.rs

1use indicatif::{ProgressBar, ProgressStyle};
2
3/// A simple indeterminate spinner for long-running operations.
4#[derive(Debug)]
5pub struct Spinner {
6    bar: ProgressBar,
7}
8
9impl Spinner {
10    /// Create and start a spinner with the given message.
11    ///
12    /// # Panics
13    ///
14    /// Panics if the embedded progress-bar template fails to compile.
15    /// The template is a string literal in this function and is exercised
16    /// by every spinner construction; a panic here is a compile-time-fixable
17    /// mistake, not a runtime data dependency.
18    #[must_use]
19    pub fn new(message: &str) -> Self {
20        let bar = ProgressBar::new_spinner();
21        #[expect(
22            clippy::expect_used,
23            reason = "static string literal template; a parse failure is a coding mistake caught at startup"
24        )]
25        bar.set_style(
26            ProgressStyle::with_template("{spinner:.green} {msg}")
27                .expect("static spinner template is well-formed")
28                .tick_strings(&[
29                    "\u{280b}", "\u{2819}", "\u{2839}", "\u{2838}", "\u{283c}", "\u{2834}",
30                    "\u{2826}", "\u{2827}", "\u{2807}", "\u{280f}", "\u{2800}",
31                ]),
32        );
33        bar.set_message(message.to_string());
34        bar.enable_steady_tick(std::time::Duration::from_millis(80));
35        Self { bar }
36    }
37
38    /// Finish the spinner with a success message.
39    pub fn finish_with_message(&self, message: &str) {
40        self.bar.finish_with_message(message.to_string());
41    }
42
43    /// Finish and clear the spinner.
44    pub fn finish_and_clear(&self) {
45        self.bar.finish_and_clear();
46    }
47}