cc-cli 0.1.6

handy git-hook and cli for conventional commits
use anyhow::{Error, Result};
use inquire::Confirm;
use std::{
    fs::{read_to_string, remove_file, set_permissions, File, Permissions},
    io::Write,
    os::unix::prelude::PermissionsExt,
    path::Path,
    process::{Command, Output},
};

const PERMISSIONS: u32 = 0o775;
const FILENAME: &str = "prepare-commit-msg";
const CONTENTS: &str = "#!/bin/sh
# cc-cli as a commit hook
exec < /dev/tty
cc-cli \"$@\"";

pub fn get_git_absolute_path() -> Result<String> {
    let Output {
        status,
        stdout,
        stderr,
    } = Command::new("git")
        .args(["rev-parse", "--absolute-git-dir"])
        .output()
        .expect("Failed run git rev-parse");

    if !status.success() {
        println!("{}", String::from_utf8(stdout).unwrap());
        println!("{}", String::from_utf8(stderr).unwrap());

        let e = Error::msg(format!(
            "Failed to find hooks dir\ngit rev-parse failed with code {}",
            status.code().unwrap()
        ));

        return Err(e);
    }

    let mut path = String::from_utf8(stdout)?;

    path.pop();

    Ok(path)
}

fn get_hook_absolute_path() -> Result<String> {
    let mut path = get_git_absolute_path()?;

    path.push_str("/hooks/");
    path.push_str(FILENAME);

    Ok(path)
}

pub fn install() -> Result<()> {
    let hooks_path = get_hook_absolute_path()?;

    let mut file = File::create(hooks_path.clone())?;
    file.write_all(CONTENTS.as_bytes())?;

    set_permissions(hooks_path, Permissions::from_mode(PERMISSIONS))?;

    println!("Hook installed for this repo!");

    Ok(())
}

pub fn uninstall() -> Result<()> {
    let hook_path = get_hook_absolute_path()?;

    if !Path::new(&hook_path).exists() {
        let e = Error::msg("Hook is not installed in this repo");
        return Err(e);
    }

    let existing_hook = read_to_string(&hook_path)?;

    if existing_hook != CONTENTS {
        let continue_deletion =
            Confirm::new("Hook installed is different than cc-cli, do you want to proceed?")
                .with_default(false)
                .prompt()?;

        if !continue_deletion {
            println!("Cancelled");
            return Ok(());
        }
    }

    remove_file(hook_path)?;
    println!("Hook uninstalled for this repo!");

    Ok(())
}

pub fn check_installed() -> Result<()> {
    let hook_path = get_hook_absolute_path()?;

    if Path::new(&hook_path).exists() {
        let existing_hook = read_to_string(&hook_path)?;

        let e = match existing_hook == CONTENTS {
            true => Error::msg("Hook is already installed! Just run 'git commit' normaly"),
            false => Error::msg("There is an existing 'prepare-commit-msg' hook"),
        };

        return Err(e);
    }

    println!("{:#?}", Path::new(&hook_path));

    Ok(())
}