turbocommit 0.5.2

A CLI tool to create commit messages with gpt-3.5-turbo
use std::cmp::Ordering;
use std::process;
use std::process::Command;

use colored::Colorize;
use inquire::MultiSelect;

use crate::openai;

pub fn check_diff(s: &str, system_len: usize, extra_len: usize) -> anyhow::Result<String> {
    let tokens_length = openai::count_token(s)?;
    match (tokens_length + system_len + extra_len).cmp(&4096_usize) {
        Ordering::Greater => {
            println!(
                "{} {}",
                "The request is too long!".red(),
                format!(
                    "The request is ~{} tokens long, while the maximum is 4096.",
                    tokens_length + system_len + extra_len
                )
                .bright_black()
            );
            let list_str = staged_files();
            let list = list_str
                .split('\n')
                .filter(|s| !s.is_empty())
                .collect::<Vec<&str>>();
            let ans = MultiSelect::new("Select the files you want to include the diff from:", list)
                .prompt();

            match ans {
                Ok(ans) => check_diff(&diff_from_files(ans), system_len, extra_len),
                Err(e) => {
                    println!("{e}");
                    process::exit(1);
                }
            }
        }
        _ => Ok(s.parse()?),
    }
}

pub fn is_repo() -> bool {
    let output = Command::new("git")
        .arg("rev-parse")
        .arg("--is-inside-work-tree")
        .output()
        .map_or_else(
            |e| {
                println!(
                    "{} {}",
                    "Error while running git:".red(),
                    format!("{}.", e).bright_black()
                );
                process::exit(1);
            },
            |o| o,
        );
    output.status.success()
}

fn staged_files() -> String {
    Command::new("git")
        .arg("diff")
        .arg("--staged")
        .arg("--name-only")
        .output()
        .map_or_else(
            |e| {
                println!(
                    "{} {}",
                    "Error while running git:".red(),
                    format!("{}.", e).bright_black()
                );
                process::exit(1);
            },
            |o| {
                String::from_utf8_lossy(&o.stdout)
                    .to_string()
                    .replace("\r\n", "\n")
            },
        )
}

pub fn diff() -> String {
    Command::new("git")
        .arg("diff")
        .arg("--staged")
        .arg("--minimal")
        .arg("-U2")
        .output()
        .map_or_else(
            |e| {
                println!(
                    "{} {}",
                    "Error while running git:".red(),
                    format!("{}.", e).bright_black()
                );
                process::exit(1);
            },
            |o| {
                String::from_utf8_lossy(&o.stdout)
                    .to_string()
                    .replace("\r\n", "\n")
            },
        )
}

fn diff_from_files(v: Vec<&str>) -> String {
    let mut binding = Command::new("git");
    let cmd = binding
        .arg("diff")
        .arg("--staged")
        .arg("--minimal")
        .arg("-U2")
        .arg("--");
    for file in v {
        cmd.arg(file);
    }
    cmd.output().map_or_else(
        |e| {
            println!(
                "{} {}",
                "Error while running git:".red(),
                format!("{}.", e).bright_black()
            );
            process::exit(1);
        },
        |o| {
            String::from_utf8_lossy(&o.stdout)
                .to_string()
                .replace("\r\n", "\n")
        },
    )
}

pub fn commit(msg: String) {
    Command::new("git")
        .arg("commit")
        .arg("-m")
        .arg(msg)
        .output()
        .map_or_else(
            |e| {
                println!(
                    "{} {}",
                    "Error while running git:".red(),
                    format!("{}.", e).bright_black()
                );
                process::exit(1);
            },
            |_| (),
        );
}