use crate::cli::AnalyzeCommands;
use std::time::Duration;
#[must_use]
pub fn format_line(command: &str, elapsed: Duration) -> String {
format!(
"⏱️ perf: {command} completed in {:.1} ms",
elapsed.as_secs_f64() * 1000.0
)
}
pub fn emit(command: &str, elapsed: Duration) {
eprintln!("{}", format_line(command, elapsed));
}
#[must_use]
pub fn format_detail_line(command: &str, name: &str, value: &str) -> String {
format!("⏱️ perf: {command} {name}: {value}")
}
pub fn emit_detail(command: &str, name: &str, value: &str) {
eprintln!("{}", format_detail_line(command, name, value));
}
#[must_use]
pub fn readout_lines(
command: &str,
elapsed: Duration,
perf: bool,
details: &[(&str, String)],
) -> Vec<String> {
if !perf {
return Vec::new();
}
let mut lines = vec![format_line(command, elapsed)];
lines.extend(
details
.iter()
.map(|(name, value)| format_detail_line(command, name, value)),
);
lines
}
pub fn emit_readout(command: &str, elapsed: Duration, perf: bool, details: &[(&str, String)]) {
for line in readout_lines(command, elapsed, perf, details) {
eprintln!("{line}");
}
}
#[must_use]
pub fn perf_command_label(cmd: &AnalyzeCommands) -> Option<&'static str> {
let (perf, label) = match cmd {
AnalyzeCommands::LintHotspot { perf, .. } => (*perf, "analyze lint-hotspot"),
AnalyzeCommands::Duplicates { perf, .. } => (*perf, "analyze duplicates"),
AnalyzeCommands::DefectPrediction { perf, .. } => (*perf, "analyze defect-prediction"),
AnalyzeCommands::Comprehensive { perf, .. } => (*perf, "analyze comprehensive"),
AnalyzeCommands::GraphMetrics { perf, .. } => (*perf, "analyze graph-metrics"),
AnalyzeCommands::NameSimilarity { perf, .. } => (*perf, "analyze name-similarity"),
AnalyzeCommands::ProofAnnotations { perf, .. } => (*perf, "analyze proof-annotations"),
AnalyzeCommands::IncrementalCoverage { perf, .. } => {
(*perf, "analyze incremental-coverage")
}
AnalyzeCommands::SymbolTable { perf, .. } => (*perf, "analyze symbol-table"),
AnalyzeCommands::BigO { perf, .. } => (*perf, "analyze big-o"),
AnalyzeCommands::AssemblyScript { perf, .. } => (*perf, "analyze assembly-script"),
AnalyzeCommands::WebAssembly { perf, .. } => (*perf, "analyze web-assembly"),
AnalyzeCommands::Clippy { perf, .. } => (*perf, "analyze clippy"),
_ => return None,
};
if perf {
Some(label)
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn readout_names_the_command_and_a_duration() {
let line = format_line("analyze big-o", Duration::from_millis(1234));
assert!(line.contains("analyze big-o"), "got: {line}");
assert!(line.contains("1234.0 ms"), "got: {line}");
assert!(line.contains("perf"), "got: {line}");
}
#[test]
fn label_is_none_without_the_flag_and_some_with_it() {
let make = |perf: bool| AnalyzeCommands::BigO {
path: std::path::PathBuf::from("."),
project_path: None,
format: crate::cli::BigOOutputFormat::Summary,
confidence_threshold: 50,
analyze_space: false,
include: vec![],
exclude: vec![],
high_complexity_only: false,
output: None,
perf,
top_files: 0,
};
assert_eq!(perf_command_label(&make(false)), None);
assert_eq!(perf_command_label(&make(true)), Some("analyze big-o"));
}
#[test]
fn readout_is_empty_without_the_flag_and_carries_every_detail_with_it() {
assert!(
readout_lines("report", Duration::from_millis(5), false, &[]).is_empty(),
"a command must stay silent when --perf was not passed"
);
let lines = readout_lines(
"report",
Duration::from_millis(5),
true,
&[("throughput", "42 files/second".to_string())],
);
assert_eq!(lines.len(), 2, "got: {lines:?}");
assert!(lines[0].contains("report"), "got: {lines:?}");
assert!(lines[0].contains("5.0 ms"), "got: {lines:?}");
assert!(
lines[1].contains("throughput: 42 files/second"),
"got: {lines:?}"
);
}
#[test]
fn every_command_with_a_perf_or_metrics_flag_is_routed() {
let routed: &[(&str, &str, &str)] = &[
("analyze", include_str!("mod.rs"), "perf_report::emit("),
(
"analyze satd",
include_str!("../satd_handler_formatting.rs"),
"fn print_metrics(",
),
(
"quality-gate",
include_str!("../../analysis_utilities/quality_gate_project.rs"),
"print_check_performance(",
),
(
"test",
include_str!("../../command_dispatcher/test_commands.rs"),
"fn print_performance_summary_if_requested(",
),
(
"report",
include_str!("../enhanced_reporting_handlers.rs"),
"perf_report::readout_lines(",
),
(
"refactor docs",
include_str!("../refactor_docs_orchestration.rs"),
"perf_report::emit_readout(",
),
];
let mut declared: Vec<String> = crate::cli::commands::on_big_stack(|| {
fn walk(cmd: &clap::Command, prefix: &str, out: &mut Vec<String>) {
let path = if prefix.is_empty() {
cmd.get_name().to_string()
} else {
format!("{prefix} {}", cmd.get_name())
};
if cmd
.get_arguments()
.any(|a| a.get_id() == "perf" || a.get_id() == "metrics")
{
out.push(path.clone());
}
for sub in cmd.get_subcommands() {
walk(sub, &path, out);
}
}
let mut out = Vec::new();
let cli = <crate::cli::Cli as clap::CommandFactory>::command();
for sub in cli.get_subcommands() {
walk(sub, "", &mut out);
}
out
});
declared.sort();
assert!(
!declared.is_empty(),
"no command declares --perf/--metrics; the probe is broken"
);
for path in &declared {
let owner = routed
.iter()
.filter(|(cmd, _, _)| path == cmd || path.starts_with(&format!("{cmd} ")))
.max_by_key(|(cmd, _, _)| cmd.len());
let Some((cmd, src, marker)) = owner else {
panic!(
"`pmat {path}` accepts --perf/--metrics but no entry in this roll call \
says where the flag is honoured, so it would parse and change nothing"
);
};
assert!(
src.contains(marker),
"`pmat {path}` is recorded as honouring the flag via `{cmd}`'s source, \
but `{marker}` is gone from it — the flag is inert again"
);
}
}
#[test]
fn every_subcommand_with_a_perf_flag_has_a_label() {
use clap::Subcommand;
let with_perf: Vec<String> = crate::cli::commands::on_big_stack(|| {
let cmd = crate::cli::commands::AnalyzeCommands::augment_subcommands(
clap::Command::new("analyze"),
);
cmd.get_subcommands()
.filter(|s| s.get_arguments().any(|a| a.get_id() == "perf"))
.map(|s| format!("analyze {}", s.get_name()))
.collect()
});
assert!(
!with_perf.is_empty(),
"no analyze subcommand declares --perf; the probe is broken"
);
let known: Vec<&str> = vec![
"analyze lint-hotspot",
"analyze duplicates",
"analyze defect-prediction",
"analyze comprehensive",
"analyze graph-metrics",
"analyze name-similarity",
"analyze proof-annotations",
"analyze incremental-coverage",
"analyze symbol-table",
"analyze big-o",
"analyze assembly-script",
"analyze web-assembly",
"analyze clippy",
];
for name in &with_perf {
assert!(
known.contains(&name.as_str()),
"{name} accepts --perf but perf_command_label() does not know it, \
so the flag would parse and change nothing"
);
}
}
}