use std::io::{IsTerminal, Write};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::thread::JoinHandle;
use std::time::Duration;
const FRAMES: [char; 10] = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
#[derive(Default)]
pub struct Counters {
pub files: AtomicU64,
pub parsed: AtomicU64,
pub cached: AtomicU64,
}
pub struct Spinner {
stop: Arc<AtomicBool>,
handle: Option<JoinHandle<()>>,
}
impl Spinner {
pub fn start(label: &'static str, counters: Arc<Counters>, quiet: bool) -> Spinner {
let stop = Arc::new(AtomicBool::new(false));
if quiet || !std::io::stderr().is_terminal() {
return Spinner { stop, handle: None };
}
let stop2 = Arc::clone(&stop);
let handle = std::thread::spawn(move || {
let mut frame = 0usize;
while !stop2.load(Ordering::Relaxed) {
let files = counters.files.load(Ordering::Relaxed);
let parsed = counters.parsed.load(Ordering::Relaxed);
let cached = counters.cached.load(Ordering::Relaxed);
let mut err = std::io::stderr().lock();
let _ = write!(
err,
"\r\u{1b}[2K{} {label} {files} files \u{b7} {parsed} parsed \u{b7} {cached} cached",
FRAMES[frame % FRAMES.len()],
);
let _ = err.flush();
frame += 1;
std::thread::sleep(Duration::from_millis(100));
}
let mut err = std::io::stderr().lock();
let _ = write!(err, "\r\u{1b}[2K");
let _ = err.flush();
});
Spinner {
stop,
handle: Some(handle),
}
}
pub fn finish(mut self) {
self.stop_inner();
}
fn stop_inner(&mut self) {
self.stop.store(true, Ordering::Relaxed);
if let Some(handle) = self.handle.take() {
let _ = handle.join();
}
}
}
impl Drop for Spinner {
fn drop(&mut self) {
self.stop_inner();
}
}