use glyphs::{style, Color};
use scoria::{Spinner, SpinnerStyle};
use std::io::{stdout, Write};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
pub fn spin(title: impl Into<String>) -> Spin {
Spin::new(title)
}
pub struct Spin {
title: String,
spinner_style: SpinnerStyle,
color: Option<Color>,
}
impl Spin {
pub fn new(title: impl Into<String>) -> Self {
Self {
title: title.into(),
spinner_style: SpinnerStyle::Dots,
color: Some(Color::Cyan),
}
}
#[must_use]
pub fn style(mut self, style: SpinnerStyle) -> Self {
self.spinner_style = style;
self
}
#[must_use]
pub fn color(mut self, color: Color) -> Self {
self.color = Some(color);
self
}
pub fn run<F, T>(self, f: F) -> T
where
F: FnOnce() -> T,
{
let running = Arc::new(AtomicBool::new(true));
let running_clone = running.clone();
let title = self.title.clone();
let spinner_style = self.spinner_style;
let color = self.color;
let spinner_thread = thread::spawn(move || {
let mut spinner = Spinner::new(spinner_style);
if let Some(c) = color {
spinner = spinner.color(c);
}
spinner = spinner.title(&title);
let mut stdout = stdout();
while running_clone.load(Ordering::Relaxed) {
print!("\r{}", spinner.view());
stdout.flush().ok();
spinner.tick();
thread::sleep(Duration::from_millis(spinner.interval_ms()));
}
print!("\r{}\r", " ".repeat(title.len() + 10));
stdout.flush().ok();
});
let result = f();
running.store(false, Ordering::Relaxed);
spinner_thread.join().ok();
println!(
"{} {}",
style("✓").fg(Color::Green),
style(&self.title).dim()
);
result
}
pub fn run_result<F, T, E>(self, f: F) -> Result<T, E>
where
F: FnOnce() -> Result<T, E>,
{
let running = Arc::new(AtomicBool::new(true));
let running_clone = running.clone();
let title = self.title.clone();
let spinner_style = self.spinner_style;
let color = self.color;
let spinner_thread = thread::spawn(move || {
let mut spinner = Spinner::new(spinner_style);
if let Some(c) = color {
spinner = spinner.color(c);
}
spinner = spinner.title(&title);
let mut stdout = stdout();
while running_clone.load(Ordering::Relaxed) {
print!("\r{}", spinner.view());
stdout.flush().ok();
spinner.tick();
thread::sleep(Duration::from_millis(spinner.interval_ms()));
}
print!("\r{}\r", " ".repeat(title.len() + 10));
stdout.flush().ok();
});
let result = f();
running.store(false, Ordering::Relaxed);
spinner_thread.join().ok();
match &result {
Ok(_) => {
println!(
"{} {}",
style("✓").fg(Color::Green),
style(&self.title).dim()
);
}
Err(_) => {
println!(
"{} {}",
style("✗").fg(Color::Red),
style(&self.title).dim()
);
}
}
result
}
}