vtcode_core/ui/
spinner.rs1use indicatif::{ProgressBar, ProgressStyle};
4use std::time::Duration;
5
6pub struct Spinner {
8 pb: ProgressBar,
9}
10
11impl Spinner {
12 pub fn new(message: &str) -> Self {
14 let pb = ProgressBar::new_spinner();
15 pb.set_style(
16 ProgressStyle::with_template("{spinner:.green} {msg}")
17 .unwrap()
18 .tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"),
19 );
20 pb.set_message(message.to_string());
21 pb.enable_steady_tick(Duration::from_millis(100));
22
23 Self { pb }
24 }
25
26 pub fn new_download_style(message: &str, total_size: u64) -> Self {
28 let pb = ProgressBar::new(total_size);
29 pb.set_style(ProgressStyle::with_template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")
30 .unwrap()
31 .progress_chars("#>-"));
32 pb.set_message(message.to_string());
33
34 Self { pb }
35 }
36
37 pub fn set_message(&self, message: &str) {
39 self.pb.set_message(message.to_string());
40 }
41
42 pub fn set_position(&self, pos: u64) {
44 self.pb.set_position(pos);
45 }
46
47 pub fn set_length(&self, len: u64) {
49 self.pb.set_length(len);
50 }
51
52 pub fn finish_with_message(&self, message: &str) {
54 self.pb.finish_with_message(message.to_string());
55 }
56
57 pub fn finish_and_clear(&self) {
59 self.pb.finish_and_clear();
60 }
61
62 pub fn finish_with_error(&self, message: &str) {
64 self.pb.abandon_with_message(format!("❌ {}", message));
65 }
66
67 pub fn clone_inner(&self) -> ProgressBar {
69 self.pb.clone()
70 }
71}
72
73pub fn show_loading_spinner(message: &str) -> Spinner {
76 let spinner = Spinner::new(message);
77 spinner.pb.tick();
78 spinner
79}
80
81pub fn start_loading_spinner(message: &str) -> Spinner {
84 let spinner = Spinner::new(message);
85 spinner.pb.tick();
86 spinner
87}
88
89pub fn start_download_spinner(message: &str, total_size: u64) -> Spinner {
92 let spinner = Spinner::new_download_style(message, total_size);
93 spinner.pb.tick();
94 spinner
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use std::thread;
101 use std::time::Duration;
102
103 #[test]
104 fn test_spinner_creation() {
105 let spinner = Spinner::new("Testing spinner");
106 assert!(spinner.clone_inner().length().is_none());
107 spinner.finish_and_clear();
108 }
109
110 #[test]
111 fn test_show_loading_spinner() {
112 let spinner = show_loading_spinner("Test message");
113 thread::sleep(Duration::from_millis(200));
114 spinner.finish_with_message("Done");
115 }
116
117 #[test]
118 fn test_download_spinner() {
119 let spinner = start_download_spinner("Downloading", 100);
120 spinner.set_position(50);
121 thread::sleep(Duration::from_millis(200));
122 spinner.finish_with_message("Download complete");
123 }
124}