use std::io::Write;
use std::sync::Arc;
use std::sync::atomic::{
AtomicBool,
Ordering
};
use std::time::Duration;
use ansi_escapes::{
CursorHide,
CursorShow,
CursorBackward,
EraseLine,
EraseStartLine,
EraseEndLine
};
use crate::{
Buffer,
Claw,
Paint,
Color
};
use crate::data::spin;
pub const CHASE: Animation = Animation::new(90, &spin::CHASE);
pub const DOT: Animation = Animation::new(60, &spin::DOTS);
pub const LOOP: Animation = Animation::new(90, &spin::LOOP);
pub const LOOP_WIDE: Animation = Animation::new(90, &spin::LOOP_WIDE);
pub const SQUARE: Animation = Animation::new(60, &spin::SQUARE);
pub const TRIANGLE: Animation = Animation::new(45, &spin::TRIANGLE);
pub trait Animate {
type State: Default + Send;
fn step(&self, state: &mut Self::State, buf: &mut Buffer) -> Duration;
fn finish(&self, state: Self::State, buf: &mut Buffer);
}
#[derive(Clone, Copy)]
pub struct Animation {
length: usize,
time_per_frame: Duration,
frames: &'static [&'static str]
}
pub(super) struct Animator<A: Animate> {
claw: Claw,
open: Buffer,
anim: Buffer,
close: Buffer,
erase: Buffer,
ctrl_buf: Buffer,
stepper: Option<Stepper<A>>,
cleared: Arc<AtomicBool>
}
struct Stepper<A: Animate> {
animation: A,
state: A::State
}
impl Animation {
pub const fn new(ms_per_frame: u64, data: &'static [&'static str]) -> Self {
Self {
length: data.len(),
time_per_frame: Duration::from_millis(ms_per_frame),
frames: data
}
}
}
impl Animate for Animation {
type State = usize;
fn step(&self, state: &mut Self::State, buf: &mut Buffer) -> Duration {
buf.push_with_color(Color::Alpha, self.frames[*state]);
*state += 1;
*state %= self.length;
self.time_per_frame
}
fn finish(&self, _: Self::State, buf: &mut Buffer) {
buf.push_with_color(Color::Alpha, " ✔ ");
}
}
impl<A: Animate> Animator<A> {
pub fn new(claw: Claw, animation: A, cleared: Arc<AtomicBool>) -> Self {
let mut open = claw.err.buffer();
open
.push(CursorHide) .push("\r")
.tag_open();
let mut close = claw.err.buffer();
close.tag_close();
let mut erase = claw.err.buffer();
write!(erase, "{EraseLine}\r").unwrap();
let mut ctrl_buf = claw.err.buffer();
write!(ctrl_buf, "{CursorHide}").unwrap();
let stepper = Some(Stepper::new(animation));
let anim = claw.err.buffer();
Self {
claw,
open,
anim,
close,
erase,
ctrl_buf,
stepper,
cleared
}
}
pub fn init(&mut self, msg_buf: &Buffer) -> Duration {
self.claw.err.print_buffer(&self.ctrl_buf).unwrap();
self.ctrl_buf.clear();
write!(self.ctrl_buf, "{EraseEndLine}{CursorShow}").unwrap();
let next_frame_in = self.stepper
.as_mut()
.unwrap()
.step(&mut self.anim);
self.write_message_line(msg_buf, None);
next_frame_in
}
pub fn step(&mut self, msg: &Buffer, input: Option<&[u8]>) -> Duration {
self.anim.clear();
let next_frame_in = self.stepper
.as_mut()
.unwrap()
.step(&mut self.anim);
self.write_message_line(msg, input);
next_frame_in
}
pub fn clear_line(&self) {
self.claw.err.print_buffer(&self.erase).unwrap();
}
pub fn print_buffer(&self, buf: Buffer) {
self.claw.out.print_buffer(&buf).unwrap();
}
pub fn write_message_line(&self, msg: &Buffer, input: Option<&[u8]>) {
self.claw.err.print_buffer(&self.open).unwrap();
self.claw.err.print_buffer(&self.anim).unwrap();
self.claw.err.print_buffer(&self.close).unwrap();
self.claw.err.print_buffer(msg).unwrap();
if let Some(input) = input {
self.claw.err.print_bytes(input).unwrap();
self.claw.err.print_buffer(&self.ctrl_buf).unwrap();
}
self.claw.err.flush().unwrap();
}
pub fn accept_input(&self, msg: &Buffer, final_bytes: &[u8]) {
if !final_bytes.is_empty() {
let mut buf = self.claw.err.buffer();
buf.write_all(final_bytes).unwrap();
self.claw.err.print_buffer(&buf).unwrap();
}
self.claw.err.print_buffer(&self.open).unwrap();
self.claw.err.print_buffer(&self.anim).unwrap();
self.claw.err.print_buffer(&self.close).unwrap();
self.claw.err.line().push(CursorBackward(1)).push(EraseStartLine);
self.write_message_line(msg, None);
}
pub fn cleanup(mut self, msg_buf: &Buffer) {
self.clear_line();
self.anim.clear();
self.stepper
.take()
.unwrap()
.finish(&mut self.anim);
self.claw.err.print_buffer(&self.open).unwrap();
self.claw.err.print_buffer(&self.anim).unwrap();
self.claw.err.print_buffer(&self.close).unwrap();
self.claw.err.print_buffer(msg_buf).unwrap();
self.ctrl_buf.clear();
writeln!(self.ctrl_buf, "{CursorShow}").unwrap();
self.claw.err.print_buffer_and_flush(&self.ctrl_buf).unwrap();
self.cleared.store(true, Ordering::Release);
}
}
impl<A: Animate> Drop for Animator<A> {
fn drop(&mut self) {
if !self.cleared.swap(true, Ordering::Acquire) {
self.ctrl_buf.clear();
write!(self.ctrl_buf, "{CursorShow}{EraseLine}\r").unwrap();
self.claw.err.print_buffer_and_flush(&self.ctrl_buf).unwrap();
}
}
}
#[cfg(feature = "tokio")]
impl Claw {
pub(super) fn clear_spinner(&self) {
let mut clear = self.err.buffer();
write!(clear, "{CursorShow}{EraseLine}\r").unwrap();
self.err.print_buffer_and_flush(&clear).unwrap();
}
}
impl<A: Animate> Stepper<A> {
fn new(animation: A) -> Self {Self {animation, state: Default::default()}}
fn step(&mut self, buffer: &mut Buffer) -> Duration {
self.animation.step(&mut self.state, buffer)
}
fn finish(self, buffer: &mut Buffer) {
self.animation.finish(self.state, buffer);
}
}