g-cli 0.1.0

Git that talks back. A human-friendly CLI wrapper for Git.
use colored::Colorize;
use dialoguer::Select;

use crate::git;

pub fn run(branch_name: &str) {
    // Warn about uncommitted changes
    let files = git::parse_status();
    if !files.is_empty() {
        let count = files.len();
        println!();
        println!(
            "  {} You have {} uncommitted change(s).",
            "".yellow(),
            count
        );

        let options = &[
            "Switch anyway (changes carry over)",
            "Cancel (save your work first)",
        ];

        let selection = Select::new()
            .with_prompt("  What would you like to do?")
            .items(options)
            .default(0)
            .interact();

        match selection {
            Ok(1) | Err(_) => {
                println!();
                println!("  Cancelled. Run {} first.", "g save \"...\"".cyan());
                println!();
                return;
            }
            _ => {}
        }
    }

    // Check if branch exists
    let branch_exists = git::branch_exists(branch_name);

    if branch_exists {
        println!();
        let result = git::run(&["checkout", branch_name]);
        if result.success {
            println!(
                "  {} Switched to '{}'",
                "".green().bold(),
                branch_name.cyan()
            );
        } else {
            println!("  {} Failed: {}", "".red(), result.stderr);
        }
        println!();
    } else {
        println!();
        println!(
            "  Branch '{}' does not exist.",
            branch_name.yellow()
        );
        println!();

        let current = git::current_branch();
        let has_main = git::branch_exists("main");
        let has_master = git::branch_exists("master");

        let mut options: Vec<String> = vec![];
        options.push(format!("Create new branch from current ({})", current));
        if has_main && current != "main" {
            options.push("Create from main".to_string());
        }
        if has_master && current != "master" {
            options.push("Create from master".to_string());
        }
        options.push("Cancel".to_string());

        let option_refs: Vec<&str> = options.iter().map(|s| s.as_str()).collect();

        let selection = Select::new()
            .with_prompt("  Options")
            .items(&option_refs)
            .default(0)
            .interact();

        let selection = match selection {
            Ok(s) => s,
            Err(_) => {
                println!("  Cancelled.");
                println!();
                return;
            }
        };

        println!();

        // Cancel is always the last option
        if selection == options.len() - 1 {
            println!("  Cancelled.");
            println!();
            return;
        }

        let from_branch = if selection == 0 {
            current.clone()
        } else {
            // selection 1+ maps to main/master based on availability
            let label = &options[selection];
            if label.contains("main") {
                "main".to_string()
            } else {
                "master".to_string()
            }
        };

        let result = git::run(&["checkout", "-b", branch_name, &from_branch]);
        if result.success {
            println!(
                "  {} Created and switched to '{}' (from {})",
                "".green().bold(),
                branch_name.cyan(),
                from_branch.dimmed()
            );
        } else {
            println!("  {} Failed: {}", "".red(), result.stderr);
        }
        println!();
    }
}