1use crate::error::Result;
6use crate::logic::{aggregate, filter, render, sort};
7use crate::model::Options;
8use crate::repo::{self, Repo, WalkedCommit};
9
10pub fn run(repo: &Repo, opts: &Options) -> Result<String> {
18 let walked = repo.walk(&opts.range, opts.reviews)?;
20
21 let mut output = String::new();
22
23 output.push_str(&stats_section(repo, opts, &walked)?);
24
25 if opts.reviews {
28 let reviews = aggregate::aggregate_reviews(walked.iter().map(|w| &w.meta), opts.email);
29 if !reviews.is_empty() {
30 output.push('\n');
31 output.push_str(&render::render_reviews(&reviews));
32 output.push('\n');
33 }
34 }
35
36 Ok(output)
37}
38
39fn stats_section(repo: &Repo, opts: &Options, walked: &[WalkedCommit]) -> Result<String> {
40 let authors = filter::compile_authors(&opts.authors)?;
42 let since = repo::parse_date(opts.since.as_deref())?;
43 let until = repo::parse_date(opts.until.as_deref())?;
44 let kept_idx = filter::keep_indices(walked.iter().map(|w| &w.meta), &authors, since, until);
45 let kept: Vec<&WalkedCommit> = kept_idx.iter().map(|&i| &walked[i]).collect();
46
47 let diffs = repo.numstats(&kept)?;
49
50 let rows: Vec<aggregate::CommitStat> = kept
52 .iter()
53 .zip(diffs)
54 .map(|(w, diff)| aggregate::CommitStat {
55 author_key: aggregate::author_key(&w.meta.author, opts.email),
56 diff,
57 })
58 .collect();
59 let mut stats = aggregate::aggregate(&rows);
60 if stats.is_empty() {
61 return Ok(String::new());
62 }
63 sort::sort_stats(&mut stats, opts.sort, opts.reverse);
64 stats.push(aggregate::compute_totals(&stats));
65
66 let mut section = render::render_stats(&stats);
67 section.push('\n');
68 Ok(section)
69}