cascade_cli/utils/
spinner.rs1use indicatif::{ProgressBar, ProgressStyle};
2use std::time::Duration;
3
4pub struct Spinner {
7 pb: ProgressBar,
8}
9
10#[derive(Debug, Clone)]
12pub struct SpinnerPrinter {
13 pb: ProgressBar,
14}
15
16impl Spinner {
17 const TICK_RATE: Duration = Duration::from_millis(80);
18 const TEMPLATE: &'static str = "{spinner:.green} {msg}";
19
20 fn new_internal(message: String) -> Self {
21 let pb = ProgressBar::new_spinner();
22 pb.set_style(
23 ProgressStyle::with_template(Self::TEMPLATE)
24 .unwrap_or_else(|_| ProgressStyle::default_spinner()),
25 );
26 pb.set_message(message);
27 pb.enable_steady_tick(Self::TICK_RATE);
28
29 std::thread::sleep(Duration::from_millis(20));
32
33 Spinner { pb }
34 }
35
36 pub fn new(message: String) -> Self {
38 Self::new_internal(message)
39 }
40
41 pub fn new_with_output_below(message: String) -> Self {
46 Self::new_internal(message)
47 }
48
49 pub fn println<T: AsRef<str>>(&self, message: T) {
51 self.pb.println(message.as_ref());
52 }
53
54 pub fn printer(&self) -> SpinnerPrinter {
57 SpinnerPrinter {
58 pb: self.pb.clone(),
59 }
60 }
61
62 pub fn suspend<F: FnOnce()>(&self, f: F) {
64 self.pb.suspend(f);
65 }
66
67 pub fn stop(&self) {
69 self.pb.finish_and_clear();
70 }
71
72 pub fn stop_with_message(&self, message: &str) {
74 self.pb.finish_with_message(message.to_string());
75 }
76
77 pub fn update_message(&self, new_message: String) {
79 self.pb.set_message(new_message);
80 }
81}
82
83impl Drop for Spinner {
84 fn drop(&mut self) {
85 if !self.pb.is_finished() {
86 self.pb.finish_and_clear();
87 }
88 }
89}
90
91impl SpinnerPrinter {
92 pub fn println<T: AsRef<str>>(&self, message: T) {
94 self.pb.println(message.as_ref());
95 }
96
97 pub fn suspend<F: FnOnce()>(&self, f: F) {
99 self.pb.suspend(f);
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use std::thread;
107
108 #[test]
109 fn test_spinner_creation_and_stop() {
110 let spinner = Spinner::new("Testing".to_string());
111 thread::sleep(Duration::from_millis(200));
112 spinner.stop();
113 }
114
115 #[test]
116 fn test_spinner_with_message() {
117 let spinner = Spinner::new("Loading".to_string());
118 thread::sleep(Duration::from_millis(200));
119 spinner.stop_with_message("✓ Done");
120 }
121}