post-push-party 0.1.11

Push code, earn points, throw a party!
use crate::party::{PartyEntry, PartyInfo, PartyRenderer};

use super::{
    Palette, RenderContext,
    style::{bold, cyan, gray, green, yellow},
};

/// computes and shows stats about pushes and commits and points
pub static STATS_PARTY: PartyEntry = PartyEntry {
    info: PartyInfo {
        id: "stats",
        name: "Stats Party",
        description: "Shows information about your commit and push trends.",
        cost: 100,
        supports_color: false,
    },
    renderer: PartyRenderer::Inline { render },
};

pub fn render(ctx: &RenderContext, _palette: &Palette) -> bool {
    let clock = ctx.clock;

    // this push
    let push_commits = ctx.push.commits();
    let push_commit_count = push_commits.len();
    let push_lines = ctx
        .push
        .commits()
        .iter()
        .map(|c| c.lines_changed())
        .sum::<u64>();
    let push_points = ctx.breakdown.total;

    // today
    let Ok(today_stats) = ctx
        .history
        .stats_since(clock.today_start(), clock.tz_offset_secs())
    else {
        return true;
    };

    // all_time
    let Ok(all_time_stats) = ctx.history.stats_since(0, clock.tz_offset_secs()) else {
        return true;
    };

    // daily_average
    let daily_avg_commit_count = all_time_stats.commits / all_time_stats.active_days;
    let daily_avg_lines = all_time_stats.lines / all_time_stats.active_days;
    let daily_avg_points = all_time_stats.points / all_time_stats.active_days;

    // calculate max widths for each column
    let w_commits = col_width([
        push_commit_count as u64,
        today_stats.commits,
        daily_avg_commit_count,
        all_time_stats.commits,
    ]);
    let w_lines = col_width([
        push_lines,
        today_stats.lines,
        daily_avg_lines,
        all_time_stats.lines,
    ]);
    let w_points = col_width([
        push_points,
        today_stats.points,
        daily_avg_points,
        all_time_stats.points,
    ]);

    // helper function to print each row of output
    let print_row = |header: &str, commits: u64, lines: u64, points: u64, extra: Option<String>| {
        let commit_word = if commits == 1 { "commit" } else { "commits" };
        let point_word = if points == 1 { "point" } else { "points" };
        let line_word = if lines == 1 { "line" } else { "lines" };

        let header = bold(format!("{header:>wh$}", wh = 10));
        let commits = format!("{} {commit_word},", green(commits));
        let lines = format!("{} {line_word} changed,", cyan(lines));
        let points = format!("{} {point_word}", yellow(points));

        let extra = extra.unwrap_or_default();

        println!(
            "{header}: {commits:<wc$} {lines:<wl$} {points:<wp$}{extra}",
            wc = w_commits + 19,
            wl = w_lines + 24,
            wp = w_points + 6
        );
    };

    println!(" Stats");
    println!(" {}", gray("─────"));

    // only show the "this push" row if there was more than one commit that counted
    if push_commit_count > 0 {
        let most_pts_ever =
            (push_points >= all_time_stats.max_points).then_some(yellow(" (biggest push ever!)"));
        print_row(
            "This Push",
            push_commit_count as u64,
            push_lines,
            push_points,
            most_pts_ever,
        );
    }

    print_row(
        "Today",
        today_stats.commits,
        today_stats.lines,
        today_stats.points,
        None,
    );
    print_row(
        "Daily Avg",
        daily_avg_commit_count,
        daily_avg_lines,
        daily_avg_points,
        None,
    );
    print_row(
        "All Time",
        all_time_stats.commits,
        all_time_stats.lines,
        all_time_stats.points,
        None,
    );

    true
}

/// helper function to compute the width of the output columns
fn col_width(vals: [u64; 4]) -> usize {
    vals.iter().map(|n| n.to_string().len()).max().unwrap_or(1)
}