git-insights 0.0.4

A CLI tool to generate Git repo stats and insights.
Documentation
use git_insights::{
    cli::{Cli, Commands, render_help, version_string},
    git::{is_git_installed, is_in_git_repo},
    output::{print_user_stats, print_user_ownership},
    stats::{
        gather_commit_stats, gather_loc_and_file_stats, gather_user_stats, run_stats,
        get_user_file_ownership,
    },
    visualize::{run_timeline_with_options, run_heatmap_with_options},
};
use std::fs::File;
use std::io::Write;

fn main() {
    // Parse CLI first so help/version work anywhere (even outside a git repo)
    let cli = match Cli::parse() {
        Ok(cli) => cli,
        Err(e) => {
            eprintln!("{}", e);
            std::process::exit(1);
        }
    };

    // Handle help/version early and exit 0
    match &cli.command {
        Commands::Help { topic } => {
            println!("{}", render_help(topic.clone()));
            return;
        }
        Commands::Version => {
            println!("{}", version_string());
            return;
        }
        _ => {}
    }

    // For all other commands we require git and a repo
    if !is_git_installed() {
        eprintln!(
            "Error: 'git' command not found. Please ensure Git is installed and in your PATH."
        );
        std::process::exit(1);
    }
    if !is_in_git_repo() {
        eprintln!("Error: Not a git repository.");
        std::process::exit(1);
    }

    match &cli.command {
        Commands::Stats { by_name } => {
            if let Err(e) = run_stats(*by_name) {
                eprintln!("Error: {}", e);
                std::process::exit(1);
            }
        }
        Commands::Json => {
            export_to_json();
        }
        Commands::User { username, ownership, by_email, top, sort } => {
            if *ownership {
                // defaults
                let top_n = top.unwrap_or(10);
                let sort_pct = sort.as_deref().map(|s| s == "pct").unwrap_or(false);
                match get_user_file_ownership(username, *by_email, top_n, sort_pct) {
                    Ok(rows) => print_user_ownership(&rows),
                    Err(e) => {
                        eprintln!("Error computing ownership: {}", e);
                        std::process::exit(1);
                    }
                }
            } else {
                get_user_insights(username);
            }
        }
        Commands::Timeline { weeks, color } => {
            let w = weeks.unwrap_or(26);
            if let Err(e) = run_timeline_with_options(w, *color) {
                eprintln!("Error: {}", e);
                std::process::exit(1);
            }
        }
        Commands::Heatmap { weeks, color } => {
            if let Err(e) = run_heatmap_with_options(*weeks, *color) {
                eprintln!("Error: {}", e);
                std::process::exit(1);
            }
        }
        // Help/Version already handled above
        _ => {}
    }
}

fn export_to_json() {
    let mut commit_stats = gather_commit_stats().expect("Failed to gather commit stats.");
    let loc_and_file_stats = gather_loc_and_file_stats().expect("Failed to gather LOC stats.");

    let mut final_stats = loc_and_file_stats;
    for (author, data) in commit_stats.drain() {
        final_stats.entry(author).or_default().commits = data.commits;
    }

    let mut json_parts = Vec::new();
    for (author, stats) in final_stats.iter() {
        json_parts.push(format!("\"{}\": {}", author, stats.to_json()));
    }
    let json_output = format!("{{\n{}\n}}", json_parts.join(",\n"));
    let mut file = File::create("git-insights.json").expect("Failed to create JSON file.");
    file.write_all(json_output.as_bytes()).expect("Failed to write JSON to file.");
    println!("Successfully exported to git-insights.json");
}

fn get_user_insights(username: &str) {
    match gather_user_stats(username) {
        Ok(stats) => {
            print_user_stats(username, &stats);
        }
        Err(e) => {
            eprintln!("Error getting user insights: {}", e);
        }
    }
}