post-push-party 0.1.7

Push code, earn points, throw a party!
Documentation
use super::{
    Palette, Party, RenderContext,
    style::{bold, cyan, gray, green, yellow},
};

/// the most basic party that shows how many points were earned
pub struct Stats;

impl Party for Stats {
    fn id(&self) -> &'static str {
        "stats"
    }

    fn name(&self) -> &'static str {
        "Stats Party"
    }

    fn description(&self) -> &'static str {
        "Shows information about your commit and push trends."
    }

    fn cost(&self) -> u64 {
        100
    }

    fn supports_color(&self) -> bool {
        false
    }

    fn render(&self, 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| {
            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));

            println!(
                "{header}: {commits:<wc$} {lines:<wl$} {points:<wp$}",
                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 {
            print_row(
                "This Push",
                push_commit_count as u64,
                push_lines,
                push_points,
            );
        }

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

        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)
}