1use std::collections::BTreeMap;
2use std::process::Command;
3
4use chrono::{NaiveDate, Utc};
5
6pub fn run(since: String) {
7 let git_log = Command::new("git")
8 .args([
9 "log",
10 "--since",
11 since.as_str(),
12 "--pretty=format:%h|%ad|%s|%an|%cr",
13 "--date=short",
14 ])
15 .output()
16 .expect("Failed to run git log");
17
18 if !git_log.status.success() {
19 eprintln!("❌ Failed to retrieve commits");
20 return;
21 }
22
23 let stdout = String::from_utf8_lossy(&git_log.stdout);
24 if stdout.trim().is_empty() {
25 println!("✅ No commits found since {since}");
26 return;
27 }
28
29 let mut grouped: BTreeMap<NaiveDate, Vec<String>> = BTreeMap::new();
30
31 for line in stdout.lines() {
32 let parts: Vec<&str> = line.splitn(5, '|').collect();
33 if parts.len() != 5 {
34 continue;
35 }
36 let date = NaiveDate::parse_from_str(parts[1], "%Y-%m-%d")
37 .unwrap_or_else(|_| Utc::now().date_naive());
38 let entry = format!(" - {} {}", emoji_for(parts[2]), parts[2].trim());
39 let meta = format!("(by {}, {})", parts[3], parts[4]);
40 grouped
41 .entry(date)
42 .or_default()
43 .push(format!("{entry} {meta}"));
44 }
45
46 println!("🗞️ Commit summary since {since}:\n");
47
48 for (date, commits) in grouped.iter().rev() {
49 println!("📅 {date}");
50 for commit in commits {
51 println!("{commit}");
52 }
53 println!();
54 }
55}
56
57fn emoji_for(message: &str) -> &str {
58 let lower = message.to_lowercase();
59 if lower.contains("fix") || lower.contains("bug") {
60 "🐛"
61 } else if lower.contains("feat") || lower.contains("add") {
62 "✨"
63 } else if lower.contains("remove") || lower.contains("delete") {
64 "🔥"
65 } else if lower.contains("refactor") {
66 "🛠"
67 } else {
68 "🔹"
69 }
70}