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 Spinner { pb }
29 }
30
31 pub fn new(message: String) -> Self {
33 Self::new_internal(message)
34 }
35
36 pub fn new_with_output_below(message: String) -> Self {
41 Self::new_internal(message)
42 }
43
44 pub fn println<T: AsRef<str>>(&self, message: T) {
46 self.pb.println(message.as_ref());
47 }
48
49 pub fn printer(&self) -> SpinnerPrinter {
52 SpinnerPrinter {
53 pb: self.pb.clone(),
54 }
55 }
56
57 pub fn suspend<F: FnOnce()>(&self, f: F) {
59 self.pb.suspend(f);
60 }
61
62 pub fn stop(&self) {
64 self.pb.finish_and_clear();
65 }
66
67 pub fn stop_with_message(&self, message: &str) {
69 self.pb.finish_with_message(message.to_string());
70 }
71
72 pub fn update_message(&self, new_message: String) {
74 self.pb.set_message(new_message);
75 }
76}
77
78impl Drop for Spinner {
79 fn drop(&mut self) {
80 if !self.pb.is_finished() {
81 self.pb.finish_and_clear();
82 }
83 }
84}
85
86impl SpinnerPrinter {
87 pub fn println<T: AsRef<str>>(&self, message: T) {
89 self.pb.println(message.as_ref());
90 }
91
92 pub fn suspend<F: FnOnce()>(&self, f: F) {
94 self.pb.suspend(f);
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use std::thread;
102
103 #[test]
104 fn test_spinner_creation_and_stop() {
105 let spinner = Spinner::new("Testing".to_string());
106 thread::sleep(Duration::from_millis(200));
107 spinner.stop();
108 }
109
110 #[test]
111 fn test_spinner_with_message() {
112 let spinner = Spinner::new("Loading".to_string());
113 thread::sleep(Duration::from_millis(200));
114 spinner.stop_with_message("✓ Done");
115 }
116}