turbocommit 0.20.1

A CLI tool to create commit messages with OpenAI GPT models
use colored::Colorize;
use crossterm::{
    cursor::MoveToColumn,
    execute,
    style::{Color, Print, ResetColor, SetForegroundColor},
    terminal::{Clear, ClearType},
};
use std::io::Write;
use tokio::time::Duration;

pub async fn start<W: Write + Send + 'static>(
    message: String,
    no_animation: bool,
    writer: W,
) -> tokio::task::JoinHandle<()> {
    let mut writer = writer;
    tokio::spawn(async move {
        if no_animation {
            writeln!(writer, "{}", message.bright_black()).ok();
            return;
        }
        let emoji_support =
            terminal_supports_emoji::supports_emoji(terminal_supports_emoji::Stream::Stdout);
        let frames = if emoji_support {
            vec![
                "🕛", "🕐", "🕑", "🕒", "🕓", "🕔", "🕕", "🕖", "🕗", "🕘", "🕙", "🕚",
            ]
        } else {
            vec!["/", "-", "\\", "|"]
        };
        let mut current_frame = 0;
        loop {
            current_frame = (current_frame + 1) % frames.len();
            match execute!(
                writer,
                Clear(ClearType::CurrentLine),
                MoveToColumn(0),
                SetForegroundColor(Color::Yellow),
                Print(message.bright_black()),
                Print(frames[current_frame]),
                ResetColor
            ) {
                Ok(_) => {}
                Err(_) => {}
            }
            tokio::time::sleep(Duration::from_millis(100)).await;
        }
    })
}

#[cfg(test)]
mod tests {

    use std::io::Result;
    use std::sync::{Arc, Mutex};

    use super::*;

    #[tokio::test]
    async fn test_animation() {
        let msg = String::from("Loading");
        let buffer = Arc::new(Mutex::new(Vec::new()));
        let writer = SharedBufferWriter {
            buffer: buffer.clone(),
        };
        let animation = start(msg, false, writer).await;
        tokio::time::sleep(Duration::from_millis(120)).await;
        animation.abort();

        let locked_buffer = buffer.lock().unwrap();
        let output = String::from_utf8(locked_buffer.clone()).unwrap();

        let expected = "\u{1b}[2K\u{1b}[1G\u{1b}[38;5;11m\u{1b}[90mLoading\u{1b}[0m🕐\u{1b}[0m\u{1b}[2K\u{1b}[1G\u{1b}[38;5;11m\u{1b}[90mLoading\u{1b}[0m🕑\u{1b}[0m";

        assert_eq!(output, expected);
    }

    struct SharedBufferWriter {
        buffer: Arc<Mutex<Vec<u8>>>,
    }

    impl Write for SharedBufferWriter {
        fn write(&mut self, buf: &[u8]) -> Result<usize> {
            let mut buffer = self.buffer.lock().unwrap();
            buffer.extend_from_slice(buf);
            Ok(buf.len())
        }

        fn flush(&mut self) -> Result<()> {
            Ok(())
        }
    }
}