1use anyhow::Result;
4use clap::Args as ClapArgs;
5use colored::Colorize;
6use std::collections::HashMap;
7
8use crate::core::{AdrStatus, ConfigManager, Git, NotesManager};
9
10#[derive(ClapArgs, Debug)]
12pub struct Args {
13 #[arg(long, short, default_value = "text")]
15 pub format: String,
16}
17
18pub fn run(args: Args) -> Result<()> {
24 let git = Git::new();
25 git.check_repository()?;
26
27 let config = ConfigManager::new(git.clone()).load()?;
28 let notes = NotesManager::new(git, config);
29
30 let adrs = notes.list()?;
31
32 let total = adrs.len();
34
35 let mut by_status: HashMap<AdrStatus, usize> = HashMap::new();
37 for adr in &adrs {
38 *by_status.entry(adr.frontmatter.status.clone()).or_insert(0) += 1;
39 }
40
41 let mut by_tag: HashMap<String, usize> = HashMap::new();
43 for adr in &adrs {
44 for tag in &adr.frontmatter.tags {
45 *by_tag.entry(tag.clone()).or_insert(0) += 1;
46 }
47 }
48
49 let dates: Vec<_> = adrs
51 .iter()
52 .filter_map(|a| a.frontmatter.date.as_ref())
53 .collect();
54 let oldest = dates.iter().min_by_key(|d| d.datetime());
55 let newest = dates.iter().max_by_key(|d| d.datetime());
56
57 if args.format.as_str() == "json" {
58 let stats = serde_json::json!({
59 "total": total,
60 "by_status": by_status.iter()
61 .map(|(k, v)| (k.to_string(), *v))
62 .collect::<HashMap<_, _>>(),
63 "by_tag": by_tag,
64 "date_range": {
65 "oldest": oldest.map(|d| d.datetime().to_rfc3339()),
66 "newest": newest.map(|d| d.datetime().to_rfc3339()),
67 }
68 });
69 println!("{}", serde_json::to_string_pretty(&stats)?);
70 } else {
71 println!("{}", "ADR Statistics".bold().underline());
72 println!();
73 println!("{} {}", "Total ADRs:".bold(), total.to_string().cyan());
74 println!();
75
76 println!("{}", "By Status:".bold());
78 let statuses = [
79 AdrStatus::Proposed,
80 AdrStatus::Accepted,
81 AdrStatus::Rejected,
82 AdrStatus::Deprecated,
83 AdrStatus::Superseded,
84 ];
85 for status in &statuses {
86 let count = by_status.get(status).unwrap_or(&0);
87 let bar = "█".repeat(*count);
88 println!(
89 " {:12} {} {}",
90 status.to_string(),
91 count.to_string().cyan(),
92 bar.green()
93 );
94 }
95 println!();
96
97 if !by_tag.is_empty() {
99 println!("{}", "Top Tags:".bold());
100 let mut tags: Vec<_> = by_tag.into_iter().collect();
101 tags.sort_by(|a, b| b.1.cmp(&a.1));
102 for (tag, count) in tags.iter().take(10) {
103 println!(" {} {}", tag.cyan(), count);
104 }
105 println!();
106 }
107
108 if let (Some(old), Some(new)) = (oldest, newest) {
110 println!("{}", "Date Range:".bold());
111 println!(
112 " {} {} to {}",
113 "From:".dimmed(),
114 old.datetime().format("%Y-%m-%d"),
115 new.datetime().format("%Y-%m-%d")
116 );
117 }
118 }
119
120 Ok(())
121}