1use crate::git::git_interop::{
2 create_consolidated_pending_read_branch, get_commits_with_notes_content,
3};
4use crate::serialization::deserialize;
5use anyhow::Result;
6use std::collections::HashSet;
7
8#[derive(Debug)]
10pub struct PendingStatus {
11 pub commit_count: usize,
13
14 pub measurement_count: usize,
16
17 pub measurement_names: HashSet<String>,
19
20 pub per_commit: Option<Vec<CommitMeasurements>>,
22}
23
24#[derive(Debug)]
26pub struct CommitMeasurements {
27 pub commit: String,
29
30 pub title: String,
32
33 pub measurement_names: Vec<String>,
35
36 pub count: usize,
38}
39
40pub fn show_status(detailed: bool) -> Result<()> {
42 let status = gather_pending_status(detailed)?;
44
45 display_status(&status, detailed)?;
47
48 Ok(())
49}
50
51pub fn gather_pending_status(detailed: bool) -> Result<PendingStatus> {
53 let pending_guard = create_consolidated_pending_read_branch()?;
57
58 let pending_ref = pending_guard.ref_name();
60
61 let commits_with_notes = get_commits_with_notes_content(pending_ref)?;
64
65 let mut commit_count = 0;
66 let mut measurement_count = 0;
67 let mut all_measurement_names = HashSet::new();
68 let mut per_commit = if detailed { Some(Vec::new()) } else { None };
69
70 for commit in &commits_with_notes {
71 let note_text = commit.note_lines.join("\n");
73 let measurements = deserialize(¬e_text);
74
75 if measurements.is_empty() {
76 continue;
77 }
78
79 commit_count += 1;
80 measurement_count += measurements.len();
81
82 let measurement_names: Vec<String> = measurements.iter().map(|m| m.name.clone()).collect();
84
85 for name in &measurement_names {
86 all_measurement_names.insert(name.clone());
87 }
88
89 if let Some(ref mut per_commit_vec) = per_commit {
91 per_commit_vec.push(CommitMeasurements {
92 commit: commit.sha.clone(),
93 title: commit.title.clone(),
94 measurement_names,
95 count: measurements.len(),
96 });
97 }
98 }
99
100 Ok(PendingStatus {
101 commit_count,
102 measurement_count,
103 measurement_names: all_measurement_names,
104 per_commit,
105 })
106}
107
108fn display_status(status: &PendingStatus, detailed: bool) -> Result<()> {
110 if status.commit_count == 0 {
111 println!("No pending measurements.");
112 println!("(use \"git perf add\" or \"git perf measure\" to add measurements)");
113 return Ok(());
114 }
115
116 println!("Pending measurements:");
117 let commit_word = if status.commit_count == 1 {
118 "commit"
119 } else {
120 "commits"
121 };
122 println!(
123 " {} {} with measurements",
124 status.commit_count, commit_word
125 );
126 let measurement_word = if status.measurement_names.len() == 1 {
127 "measurement"
128 } else {
129 "measurements"
130 };
131 println!(
132 " {} unique {}",
133 status.measurement_names.len(),
134 measurement_word
135 );
136 println!();
137
138 if !status.measurement_names.is_empty() {
139 println!("Measurement names:");
140 let mut sorted_names: Vec<_> = status.measurement_names.iter().collect();
141 sorted_names.sort();
142 for name in sorted_names {
143 println!(" - {}", name);
144 }
145 println!();
146 }
147
148 if detailed {
149 if let Some(ref per_commit) = status.per_commit {
150 println!("Per-commit breakdown:");
151 for commit_info in per_commit {
152 let short_sha = if commit_info.commit.len() >= 12 {
153 &commit_info.commit[..12]
154 } else {
155 &commit_info.commit
156 };
157 let meas_word = if commit_info.count == 1 {
158 "measurement"
159 } else {
160 "measurements"
161 };
162 println!(
163 " {} ({} {}) - {}",
164 short_sha, commit_info.count, meas_word, commit_info.title
165 );
166 for name in &commit_info.measurement_names {
167 println!(" - {}", name);
168 }
169 }
170 println!();
171 }
172 }
173
174 println!("(use \"git perf reset\" to discard pending measurements)");
175 println!("(use \"git perf push\" to publish measurements)");
176
177 Ok(())
178}