use std::collections::BTreeMap; use chrono::{DateTime, Datelike, Duration, Utc}; use clap::ValueEnum; use crate::{get_char, get_color, get_color_map, Commit, DAYS, RESET}; pub struct Heatmap { data: [Vec; 7], start_date: DateTime, end_date: DateTime, commits: Vec, months: Vec<(usize, String)>, } impl Heatmap { pub fn new(start_date: DateTime, end_date: DateTime, commits: Vec) -> Self { let mut heatmap = Self { data: [vec![], vec![], vec![], vec![], vec![], vec![], vec![]], start_date, end_date, commits, months: vec![], }; let mut grouped_commits = BTreeMap::new(); for commit in &heatmap.commits { let commit_day = commit.time.ordinal0(); let record = grouped_commits.entry(commit_day).or_insert(0); *record += 1; } let mut current_day = start_date; let mut day_of_week = (current_day.weekday().num_days_from_monday() + 1) % 7; if day_of_week != 0 { for i in 0..day_of_week { heatmap.data[i as usize].push(0); } } while current_day <= end_date { let month_name = current_day.format("%b").to_string(); if current_day == start_date { heatmap.months.push((0, month_name)); } else if current_day.day0() == 0 { heatmap .months .push((heatmap.data[day_of_week as usize].len(), month_name)); } let value = grouped_commits.get(¤t_day.ordinal0()); match value { Some(val) => heatmap.data[day_of_week as usize].push(*val), None => heatmap.data[day_of_week as usize].push(0), } current_day += Duration::days(1); day_of_week = (current_day.weekday().num_days_from_monday() + 1) % 7; // println!("{} - {current_day} | {value:?}", current_day.ordinal0()); } heatmap } fn months_row(&self) -> String { let mut row = " ".to_string(); let mut last_index = 0; for (index, month) in &self.months { let range_size = (index * 2).saturating_sub(last_index * 2).saturating_sub(3); for _i in 0..range_size { row.push(' '); } last_index = *index; row.push_str(month); } row } pub fn print(&self) { println!( "{} - {}", self.start_date.format("%Y-%b-%d"), self.end_date.format("%Y-%b-%d") ); println!("{} commits\n", &self.commits.len()); println!("{}", self.months_row()); for (day, row) in DAYS.iter().zip(&self.data) { print!("{day} "); for val in row { let color = &get_color_map()[get_color(*val)]; print!("{color}{}{RESET} ", get_char()); } print!("\n"); } print!("\nLess "); for color in get_color_map() { print!("{color}{}{RESET} ", get_char()) } print!(" More\n"); } } #[derive(Clone, Debug, PartialEq, Eq, ValueEnum)] pub enum HeatmapColors { Green, Red, }