use crate::git::git_interop::{
create_consolidated_pending_read_branch, get_commit_details, get_commits_with_notes,
get_notes_for_commit,
};
use crate::serialization::deserialize;
use anyhow::Result;
use std::collections::HashSet;
#[derive(Debug)]
pub struct PendingStatus {
pub commit_count: usize,
pub measurement_count: usize,
pub measurement_names: HashSet<String>,
pub per_commit: Option<Vec<CommitMeasurements>>,
}
#[derive(Debug)]
pub struct CommitMeasurements {
pub commit: String,
pub title: String,
pub measurement_names: Vec<String>,
pub count: usize,
}
pub fn show_status(detailed: bool) -> Result<()> {
let status = gather_pending_status(detailed)?;
display_status(&status, detailed)?;
Ok(())
}
pub fn gather_pending_status(detailed: bool) -> Result<PendingStatus> {
let pending_guard = create_consolidated_pending_read_branch()?;
let pending_ref = pending_guard.ref_name();
let pending_commits = get_commits_with_notes(pending_ref)?;
let mut commit_count = 0;
let mut measurement_count = 0;
let mut all_measurement_names = HashSet::new();
let mut per_commit = if detailed { Some(Vec::new()) } else { None };
for commit_sha in &pending_commits {
let note_lines = get_notes_for_commit(pending_ref, commit_sha)?;
if note_lines.is_empty() {
continue;
}
let note_text = note_lines.join("\n");
let measurements = deserialize(¬e_text);
if measurements.is_empty() {
continue;
}
commit_count += 1;
measurement_count += measurements.len();
let measurement_names: Vec<String> = measurements.iter().map(|m| m.name.clone()).collect();
for name in &measurement_names {
all_measurement_names.insert(name.clone());
}
if let Some(ref mut per_commit_vec) = per_commit {
let commit_details = get_commit_details(std::slice::from_ref(commit_sha))?;
if let Some(commit_info) = commit_details.first() {
per_commit_vec.push(CommitMeasurements {
commit: commit_sha.clone(),
title: commit_info.title.clone(),
measurement_names: measurement_names.clone(),
count: measurements.len(),
});
}
}
}
Ok(PendingStatus {
commit_count,
measurement_count,
measurement_names: all_measurement_names,
per_commit,
})
}
fn display_status(status: &PendingStatus, detailed: bool) -> Result<()> {
if status.commit_count == 0 {
println!("No pending measurements.");
println!("(use \"git perf add\" or \"git perf measure\" to add measurements)");
return Ok(());
}
println!("Pending measurements:");
let commit_word = if status.commit_count == 1 {
"commit"
} else {
"commits"
};
println!(
" {} {} with measurements",
status.commit_count, commit_word
);
let measurement_word = if status.measurement_names.len() == 1 {
"measurement"
} else {
"measurements"
};
println!(
" {} unique {}",
status.measurement_names.len(),
measurement_word
);
println!();
if !status.measurement_names.is_empty() {
println!("Measurement names:");
let mut sorted_names: Vec<_> = status.measurement_names.iter().collect();
sorted_names.sort();
for name in sorted_names {
println!(" - {}", name);
}
println!();
}
if detailed {
if let Some(ref per_commit) = status.per_commit {
println!("Per-commit breakdown:");
for commit_info in per_commit {
let short_sha = if commit_info.commit.len() >= 12 {
&commit_info.commit[..12]
} else {
&commit_info.commit
};
let meas_word = if commit_info.count == 1 {
"measurement"
} else {
"measurements"
};
println!(
" {} ({} {}) - {}",
short_sha, commit_info.count, meas_word, commit_info.title
);
for name in &commit_info.measurement_names {
println!(" - {}", name);
}
}
println!();
}
}
println!("(use \"git perf reset\" to discard pending measurements)");
println!("(use \"git perf push\" to publish measurements)");
Ok(())
}