use std::cell::RefCell;
use std::rc::Rc;
use std::sync::mpsc::{channel, Sender, TryRecvError};
use std::thread::{sleep, spawn, JoinHandle};
use std::time::Duration;
use crate::state::{State, Update};
use crate::term;
#[derive(Debug, Clone)]
pub struct Spinner<S> {
update: RefCell<Update>,
state: S,
}
#[derive(Clone, Debug)]
pub struct Stopped;
#[derive(Clone, Debug)]
pub struct Running {
sender: Sender<Update>,
handle: Rc<RefCell<Option<JoinHandle<()>>>>,
}
pub type RunningSpinner = Spinner<Running>;
pub type StoppedSpinner = Spinner<Stopped>;
impl<S> Spinner<S> {
pub fn color(&self, color: term::Color) -> &Self {
self.update.borrow_mut().color = Some(color);
self
}
pub fn text(&self, text: &str) -> &Self {
self.update.borrow_mut().text = Some(text.to_string());
self
}
pub fn symbols(&self, symbols: Vec<&'static str>) -> &Self {
self.update.borrow_mut().symbols = Some(symbols);
self
}
pub fn symbol(&self, symbol: &'static str) -> &Self {
self.update.borrow_mut().symbols = Some(vec![symbol]);
self
}
pub fn frames_duration(&self, ms: u64) -> &Self {
self.update.borrow_mut().frames_duration_ms = Some(ms);
self
}
}
impl Spinner<Stopped> {
#[must_use]
pub fn new(text: &str) -> Self {
Spinner {
update: RefCell::new(Update::new(text)),
state: Stopped,
}
}
pub fn start(&self) -> Spinner<Running> {
term::hide_cursor();
let (sender, receiver) = channel::<Update>();
let mut state = State::default();
state.update(self.update.take());
let handle = RefCell::new(Some(spawn(move || {
let mut iteration = 0;
loop {
match receiver.try_recv() {
Ok(update) if update.stop => {
state.update(update);
if iteration >= state.symbols.len() {
iteration = 0;
}
state.render(iteration);
break;
}
Ok(update) => state.update(update),
Err(TryRecvError::Disconnected) => break,
Err(TryRecvError::Empty) => (),
}
if iteration >= state.symbols.len() {
iteration = 0;
}
state.render(iteration);
iteration += 1;
sleep(Duration::from_millis(state.frames_duration_ms));
}
term::new_line();
term::show_cursor();
})));
let handle = Rc::new(handle);
Spinner {
update: RefCell::new(Update::default()),
state: Running { sender, handle },
}
}
}
impl Spinner<Running> {
fn join(&self) {
if let Some(handle) = self.state.handle.borrow_mut().take() {
_ = handle.join();
}
}
pub fn update(&self) -> &Self {
_ = self.state.sender.send(self.update.borrow().clone());
self
}
pub fn stop(&self) {
self.update.borrow_mut().stop = true;
self.update();
self.join();
}
pub fn success(&self) {
self.update.borrow_mut().color = Some(term::Color::Green);
self.update.borrow_mut().symbols = Some(vec!["✔"]);
self.stop();
}
pub fn failure(&self) {
self.update.borrow_mut().color = Some(term::Color::Red);
self.update.borrow_mut().symbols = Some(vec!["✖"]);
self.stop();
}
pub fn warn(&self) {
self.update.borrow_mut().color = Some(term::Color::Yellow);
self.update.borrow_mut().symbols = Some(vec!["⚠"]);
self.stop();
}
}