use indicatif::{ProgressBar, ProgressStyle};
use std::time::Duration;
pub struct Spinner {
pb: ProgressBar,
}
impl Spinner {
pub fn new(message: &str) -> Self {
let pb = ProgressBar::new_spinner();
pb.set_style(
ProgressStyle::with_template("{spinner:.green} {msg}")
.unwrap()
.tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"),
);
pb.set_message(message.to_string());
pb.enable_steady_tick(Duration::from_millis(100));
pb.tick();
Self { pb }
}
pub fn new_download_style(message: &str, total_size: u64) -> Self {
let pb = ProgressBar::new(total_size);
pb.set_style(ProgressStyle::with_template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")
.unwrap()
.progress_chars("#>-"));
pb.set_message(message.to_string());
Self { pb }
}
pub fn set_message(&self, message: &str) {
self.pb.set_message(message.to_string());
}
pub fn set_position(&self, pos: u64) {
self.pb.set_position(pos);
}
pub fn set_length(&self, len: u64) {
self.pb.set_length(len);
}
pub fn finish_with_message(&self, message: &str) {
self.pb.finish_with_message(message.to_string());
}
pub fn finish_and_clear(&self) {
self.pb.finish_and_clear();
}
pub fn finish_with_error(&self, message: &str) {
self.pb.abandon_with_message(format!("❌ {}", message));
}
pub fn clone_inner(&self) -> ProgressBar {
self.pb.clone()
}
pub fn new_async(message: &str) -> Self {
let pb = ProgressBar::new_spinner();
pb.set_style(
ProgressStyle::with_template("{spinner:.green} {msg}")
.unwrap()
.tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"),
);
pb.set_message(message.to_string());
pb.enable_steady_tick(Duration::from_millis(100));
pb.tick();
Self { pb }
}
pub fn spawn_async(self) -> tokio::task::JoinHandle<()> {
tokio::spawn(async move {
loop {
tokio::time::sleep(Duration::from_millis(100)).await;
}
})
}
}
pub fn show_loading_spinner(message: &str) -> Spinner {
let spinner = Spinner::new(message);
spinner.pb.tick();
spinner
}
pub fn start_loading_spinner(message: &str) -> Spinner {
let spinner = Spinner::new(message);
spinner.pb.tick();
spinner
}
pub fn start_download_spinner(message: &str, total_size: u64) -> Spinner {
let spinner = Spinner::new_download_style(message, total_size);
spinner.pb.tick();
spinner
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
use std::time::Duration;
#[test]
fn test_spinner_creation() {
let spinner = Spinner::new("Testing spinner");
assert!(spinner.clone_inner().length().is_none());
spinner.finish_and_clear();
}
#[test]
fn test_show_loading_spinner() {
let spinner = show_loading_spinner("Test message");
thread::sleep(Duration::from_millis(200));
spinner.finish_with_message("Done");
}
#[test]
fn test_download_spinner() {
let spinner = start_download_spinner("Downloading", 100);
spinner.set_position(50);
thread::sleep(Duration::from_millis(200));
spinner.finish_with_message("Download complete");
}
}