#![cfg_attr(coverage_nightly, coverage(off))]
use crate::cli::{BigOOutputFormat, Path};
use crate::services::big_o_analyzer::{BigOAnalysisConfig, BigOAnalyzer};
use anyhow::Result;
use std::path::PathBuf;
use tracing::{debug, info};
use super::filters::apply_report_filters;
use super::output::{format_analysis_output, write_analysis_output};
pub(super) const ANALYZE_SPACE_NOOP_NOTE: &str =
"note: --analyze-space is a no-op — space complexity is always reported alongside time";
#[allow(clippy::too_many_arguments)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_analyze_big_o(
project_path: PathBuf,
format: BigOOutputFormat,
confidence_threshold: u8,
analyze_space: bool,
include: Vec<String>,
exclude: Vec<String>,
high_complexity_only: bool,
output: Option<PathBuf>,
perf: bool,
top_files: usize,
) -> Result<()> {
crate::cli::ensure_analysis_path_exists(&project_path)?;
let start_time = std::time::Instant::now();
if analyze_space {
eprintln!("{ANALYZE_SPACE_NOOP_NOTE}");
}
print_analysis_header(&project_path, confidence_threshold);
let config = build_analysis_config(
project_path,
include,
exclude,
confidence_threshold,
analyze_space,
);
if perf {
debug!("Analysis configuration: {:?}", config);
}
let analyzer = BigOAnalyzer::new();
let mut report = analyzer.analyze(config).await?;
apply_report_filters(&mut report, high_complexity_only, top_files, perf);
let output_content = format_analysis_output(&analyzer, &report, format, high_complexity_only)?;
write_analysis_output(&output_content, output).await?;
print_analysis_summary(&report, start_time.elapsed(), perf);
Ok(())
}
pub(super) fn print_analysis_header(project_path: &Path, confidence_threshold: u8) {
info!("🔍 Starting Big-O complexity analysis");
info!("📂 Project path: {}", project_path.display());
info!("🎯 Confidence threshold: {}%", confidence_threshold);
}
pub(super) fn build_analysis_config(
project_path: PathBuf,
include: Vec<String>,
exclude: Vec<String>,
confidence_threshold: u8,
analyze_space: bool,
) -> BigOAnalysisConfig {
BigOAnalysisConfig {
project_path,
include_patterns: include,
exclude_patterns: exclude,
confidence_threshold,
analyze_space_complexity: analyze_space,
}
}
pub(super) fn print_analysis_summary(
report: &crate::services::big_o_analyzer::BigOAnalysisReport,
elapsed: std::time::Duration,
perf: bool,
) {
info!("✅ Big-O analysis completed in {:?}", elapsed);
info!("📊 Analyzed {} functions", report.analyzed_functions);
if !report.high_complexity_functions.is_empty() {
info!(
"⚠️ Found {} functions with high complexity",
report.high_complexity_functions.len()
);
}
if perf {
let functions_per_sec = report.analyzed_functions as f64 / elapsed.as_secs_f64();
crate::cli::handlers::analysis_handlers::perf_report::emit_detail(
"analyze big-o",
"throughput",
&format!("{functions_per_sec:.0} functions/second"),
);
}
}
#[cfg(test)]
mod analyze_space_noop_tests {
use super::*;
#[test]
fn the_noop_note_says_the_flag_is_a_no_op() {
assert!(ANALYZE_SPACE_NOOP_NOTE.contains("--analyze-space"));
assert!(ANALYZE_SPACE_NOOP_NOTE.contains("no-op"));
assert!(ANALYZE_SPACE_NOOP_NOTE.contains("always reported"));
}
#[test]
fn help_text_does_not_promise_an_extra_analysis() {
use clap::Subcommand;
let help = crate::cli::commands::on_big_stack(|| {
let cmd = crate::cli::commands::AnalyzeCommands::augment_subcommands(
clap::Command::new("analyze"),
);
let help = cmd
.get_subcommands()
.find(|s| s.get_name() == "big-o")
.expect("big-o subcommand must exist")
.get_arguments()
.find(|a| a.get_id() == "analyze_space")
.expect("--analyze-space must exist")
.get_help()
.map(std::string::ToString::to_string)
.unwrap_or_default();
help
});
assert!(
help.contains("NO-OP"),
"help must state the flag is inert, got: {help}"
);
assert!(
!help.contains("in addition to time"),
"help must not promise an analysis the flag does not enable, got: {help}"
);
}
}