use crate::symbols::frames::DOTS;
use std::io::{self, Write};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::{self, JoinHandle};
use std::time::Duration;
pub use crate::symbols::frames;
#[derive(Debug)]
pub struct Spinner {
message: String,
frames: &'static [&'static str],
interval: Duration,
running: Arc<AtomicBool>,
handle: Option<JoinHandle<()>>,
}
impl Spinner {
pub fn new(message: impl Into<String>) -> Self {
Spinner {
message: message.into(),
frames: DOTS,
interval: Duration::from_millis(80),
running: Arc::new(AtomicBool::new(false)),
handle: None,
}
}
pub fn with_frames(mut self, frames: &'static [&'static str]) -> Self {
self.frames = frames;
self
}
pub fn with_interval(mut self, interval: Duration) -> Self {
self.interval = interval;
self
}
pub fn start(&mut self) {
if self.running.load(Ordering::SeqCst) {
return;
}
self.running.store(true, Ordering::SeqCst);
let running = Arc::clone(&self.running);
let frames = self.frames;
let message = self.message.clone();
let interval = self.interval;
let handle = thread::spawn(move || {
let mut i = 0usize;
while running.load(Ordering::SeqCst) {
let frame = frames[i % frames.len()];
print!("\r{} {}", frame, message);
let _ = io::stdout().flush();
thread::sleep(interval);
i = i.wrapping_add(1);
}
});
self.handle = Some(handle);
}
pub fn set_message(&mut self, message: impl Into<String>) {
self.message = message.into();
}
pub fn stop(&mut self, final_message: impl AsRef<str>) {
self.running.store(false, Ordering::SeqCst);
if let Some(handle) = self.handle.take() {
let _ = handle.join();
}
print!("\r\x1b[2K");
println!("{}", final_message.as_ref());
let _ = io::stdout().flush();
}
}
impl Drop for Spinner {
fn drop(&mut self) {
self.running.store(false, Ordering::SeqCst);
if let Some(handle) = self.handle.take() {
let _ = handle.join();
}
}
}