use crate::cli::{BigOOutputFormat, Path};
use crate::services::big_o_analyzer::{BigOAnalysisConfig, BigOAnalyzer};
use anyhow::Result;
use std::path::PathBuf;
use tracing::{debug, info};
#[allow(clippy::too_many_arguments)]
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<()> {
let start_time = std::time::Instant::now();
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)?;
write_analysis_output(&output_content, output).await?;
print_analysis_summary(&report, start_time.elapsed(), perf);
Ok(())
}
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);
}
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,
}
}
fn apply_report_filters(
report: &mut crate::services::big_o_analyzer::BigOAnalysisReport,
high_complexity_only: bool,
top_files: usize,
perf: bool,
) {
if high_complexity_only {
apply_high_complexity_filter(report, perf);
}
if top_files > 0 {
apply_top_files_filter(report, top_files);
}
}
fn apply_high_complexity_filter(
report: &mut crate::services::big_o_analyzer::BigOAnalysisReport,
perf: bool,
) {
let original_count = report.high_complexity_functions.len();
report
.high_complexity_functions
.retain(|f| is_high_complexity_class(&f.time_complexity.class));
if perf {
debug!(
"Filtered from {} to {} high complexity functions",
original_count,
report.high_complexity_functions.len()
);
}
}
fn is_high_complexity_class(class: &crate::models::complexity_bound::BigOClass) -> bool {
matches!(
class,
crate::models::complexity_bound::BigOClass::Quadratic
| crate::models::complexity_bound::BigOClass::Cubic
| crate::models::complexity_bound::BigOClass::Exponential
| crate::models::complexity_bound::BigOClass::Factorial
)
}
fn apply_top_files_filter(
report: &mut crate::services::big_o_analyzer::BigOAnalysisReport,
top_files: usize,
) {
let file_functions = group_functions_by_file(&report.high_complexity_functions);
let file_scores = calculate_file_complexity_scores(&file_functions);
let top_file_paths = get_top_file_paths(file_scores, top_files);
report
.high_complexity_functions
.retain(|f| top_file_paths.contains(&f.file_path));
}
fn group_functions_by_file(
functions: &[crate::services::big_o_analyzer::FunctionComplexity],
) -> std::collections::HashMap<PathBuf, Vec<crate::services::big_o_analyzer::FunctionComplexity>> {
use std::collections::HashMap;
let mut file_functions: HashMap<PathBuf, Vec<_>> = HashMap::new();
for func in functions.iter().cloned() {
file_functions
.entry(func.file_path.clone())
.or_default()
.push(func);
}
file_functions
}
fn calculate_file_complexity_scores(
file_functions: &std::collections::HashMap<
PathBuf,
Vec<crate::services::big_o_analyzer::FunctionComplexity>,
>,
) -> Vec<(PathBuf, f64)> {
let mut file_scores: Vec<(PathBuf, f64)> = file_functions
.iter()
.map(|(path, funcs)| {
let score: f64 = funcs
.iter()
.map(|f| get_complexity_class_score(&f.time_complexity.class))
.sum();
(path.clone(), score)
})
.collect();
file_scores.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
file_scores
}
fn get_complexity_class_score(class: &crate::models::complexity_bound::BigOClass) -> f64 {
match class {
crate::models::complexity_bound::BigOClass::Constant => 1.0,
crate::models::complexity_bound::BigOClass::Logarithmic => 2.0,
crate::models::complexity_bound::BigOClass::Linear => 3.0,
crate::models::complexity_bound::BigOClass::Linearithmic => 4.0,
crate::models::complexity_bound::BigOClass::Quadratic => 5.0,
crate::models::complexity_bound::BigOClass::Cubic => 6.0,
crate::models::complexity_bound::BigOClass::Exponential => 7.0,
crate::models::complexity_bound::BigOClass::Factorial => 8.0,
crate::models::complexity_bound::BigOClass::Unknown => 3.0,
}
}
fn get_top_file_paths(
file_scores: Vec<(PathBuf, f64)>,
top_files: usize,
) -> std::collections::HashSet<PathBuf> {
file_scores
.into_iter()
.take(top_files)
.map(|(path, _)| path)
.collect()
}
fn format_analysis_output(
analyzer: &BigOAnalyzer,
report: &crate::services::big_o_analyzer::BigOAnalysisReport,
format: BigOOutputFormat,
) -> Result<String> {
match format {
BigOOutputFormat::Json => analyzer.format_as_json(report),
BigOOutputFormat::Markdown => Ok(analyzer.format_as_markdown(report)),
BigOOutputFormat::Summary => Ok(format_big_o_summary(report)),
BigOOutputFormat::Detailed => Ok(format_big_o_detailed(report)),
}
}
async fn write_analysis_output(content: &str, output: Option<PathBuf>) -> Result<()> {
if let Some(output_path) = output {
tokio::fs::write(&output_path, content).await?;
info!("📄 Big-O analysis saved to: {}", output_path.display());
} else {
println!("{content}");
}
Ok(())
}
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();
info!("⚡ Performance: {:.0} functions/second", functions_per_sec);
}
}
#[must_use]
pub fn format_big_o_summary(
report: &crate::services::big_o_analyzer::BigOAnalysisReport,
) -> String {
let mut output = String::with_capacity(1024);
output.push_str("Big-O Complexity Analysis Summary\n");
output.push_str("=================================\n\n");
output.push_str(&format!(
"Total Functions Analyzed: {}\n",
report.analyzed_functions
));
output.push_str(&format!(
"High Complexity Functions: {}\n\n",
report.high_complexity_functions.len()
));
output.push_str("Complexity Distribution:\n");
let dist = &report.complexity_distribution;
output.push_str(&format!(" O(1) : {:>4} functions\n", dist.constant));
output.push_str(&format!(
" O(log n) : {:>4} functions\n",
dist.logarithmic
));
output.push_str(&format!(" O(n) : {:>4} functions\n", dist.linear));
output.push_str(&format!(
" O(n log n) : {:>4} functions\n",
dist.linearithmic
));
output.push_str(&format!(" O(n²) : {:>4} functions\n", dist.quadratic));
output.push_str(&format!(" O(n³) : {:>4} functions\n", dist.cubic));
output.push_str(&format!(
" O(2^n) : {:>4} functions\n",
dist.exponential
));
output.push_str(&format!(" Unknown : {:>4} functions\n", dist.unknown));
if !report.recommendations.is_empty() {
output.push_str("\nRecommendations:\n");
for rec in &report.recommendations {
output.push_str(&format!("• {rec}\n"));
}
}
if !report.high_complexity_functions.is_empty() {
output.push_str("\nTop Files by Complexity:\n");
use std::collections::HashMap;
let mut file_scores: HashMap<&std::path::Path, f64> = HashMap::new();
let mut file_function_counts: HashMap<&std::path::Path, usize> = HashMap::new();
for func in &report.high_complexity_functions {
let score = match func.time_complexity.class {
crate::models::complexity_bound::BigOClass::Constant => 1.0,
crate::models::complexity_bound::BigOClass::Logarithmic => 2.0,
crate::models::complexity_bound::BigOClass::Linear => 3.0,
crate::models::complexity_bound::BigOClass::Linearithmic => 4.0,
crate::models::complexity_bound::BigOClass::Quadratic => 5.0,
crate::models::complexity_bound::BigOClass::Cubic => 6.0,
crate::models::complexity_bound::BigOClass::Exponential => 7.0,
crate::models::complexity_bound::BigOClass::Factorial => 8.0,
crate::models::complexity_bound::BigOClass::Unknown => 3.0,
};
*file_scores.entry(&func.file_path).or_insert(0.0) += score;
*file_function_counts.entry(&func.file_path).or_insert(0) += 1;
}
let mut sorted_files: Vec<_> = file_scores.into_iter().collect();
sorted_files.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
for (i, (file_path, score)) in sorted_files.iter().take(10).enumerate() {
let filename = file_path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or(file_path.to_str().unwrap_or("unknown"));
let function_count = file_function_counts.get(file_path).unwrap_or(&0);
output.push_str(&format!(
" {}. {} - score: {:.1}, {} functions\n",
i + 1,
filename,
score,
function_count
));
}
}
output
}
fn format_big_o_detailed(report: &crate::services::big_o_analyzer::BigOAnalysisReport) -> String {
let mut output = format_big_o_summary(report);
if !report.high_complexity_functions.is_empty() {
output.push_str("\nHigh Complexity Functions:\n");
output.push_str("==========================\n");
for func in &report.high_complexity_functions {
output.push_str(&format!(
"\n{} ({}:{})\n",
func.function_name,
func.file_path.display(),
func.line_number
));
output.push_str(&format!(
" Time Complexity: {} ({}% confidence)\n",
func.time_complexity.notation(),
func.time_complexity.confidence
));
output.push_str(&format!(
" Space Complexity: {} ({}% confidence)\n",
func.space_complexity.notation(),
func.space_complexity.confidence
));
if !func.notes.is_empty() {
output.push_str(" Notes:\n");
for note in &func.notes {
output.push_str(&format!(" - {note}\n"));
}
}
}
}
if !report.pattern_matches.is_empty() {
output.push_str("\nPattern Matches:\n");
output.push_str("================\n");
for pattern in &report.pattern_matches {
output.push_str(&format!(
" {} : {} occurrences\n",
pattern.pattern_name, pattern.occurrences
));
}
}
output
}
#[cfg(test)]
mod tests {
#[test]
fn test_big_o_handlers_basic() {
assert_eq!(1 + 1, 2);
}
}
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}