term-kit 0.2.5

A simple terminal UI toolkit for Rust
Documentation
pub use crate::spinner::LabelPosition::*;
use crossterm::cursor::Hide;
use crossterm::cursor::Show;
use crossterm::terminal::Clear;
use crossterm::{cursor, execute, style::Print, terminal::ClearType};
use std::io::{stdout, Write};
use std::sync::{
    atomic::{AtomicBool, Ordering},
    Arc,
};
use std::thread;
use std::time::Duration;

#[derive(Clone)]
pub enum LabelPosition {
    Before,
    After,
}

pub struct Spinner {
    frames: Vec<&'static str>,
    is_spinning: Arc<AtomicBool>,
    position: (u16, u16),
    label: Option<String>,
    label_position: LabelPosition,
}

impl Spinner {
    pub fn new() -> Self {
        let (x, y) = cursor::position().unwrap();
        Self {
            frames: vec!["", "", "", "", "", "", "", "", "", ""],
            is_spinning: Arc::new(AtomicBool::new(false)),
            position: (x, y),
            label: None,
            label_position: After,
        }
    }

    pub fn with_label(mut self, label: String) -> Self {
        self.label = Some(label);
        self
    }

    pub fn with_label_position(mut self, label_position: LabelPosition) -> Self {
        self.label_position = label_position;
        self
    }

    pub fn start(&self) {
        let is_spinning = self.is_spinning.clone();
        let position: (u16, u16) = self.position;
        let frames = self.frames.clone();
        let label = self.label.clone().unwrap_or("".to_string());
        let label_position = self.label_position.clone();

        is_spinning.store(true, Ordering::Relaxed);

        thread::spawn(move || {
            let mut current_frame = 0;
            while is_spinning.load(Ordering::Relaxed) {
                let frame = frames[current_frame];
                let mut stdout = stdout();
                execute!(
                    stdout,
                    Hide,
                    cursor::MoveTo(position.0, position.1),
                    Clear(ClearType::CurrentLine),
                    match label_position {
                        Before => {
                            Print(format!(" {} {}", label, frame))
                        }
                        After => {
                            Print(format!(" {} {}", frame, label))
                        }
                    }
                )
                .unwrap();
                stdout.flush().unwrap();
                current_frame = (current_frame + 1) % frames.len();
                thread::sleep(Duration::from_millis(100));
            }
        });
    }

    pub fn stop(&self) {
        execute!(
            stdout(),
            Show,
            cursor::MoveTo(self.position.0, self.position.1),
            Clear(ClearType::CurrentLine)
        )
        .unwrap();
        self.is_spinning.store(false, Ordering::Relaxed);
    }
}