#![cfg_attr(coverage_nightly, coverage(off))]
use crate::cli::{
ComprehensiveOutputFormat, DagType, DeepContextOutputFormat, DefectPredictionOutputFormat,
GraphMetricType, GraphMetricsOutputFormat, MakefileOutputFormat, SymbolTableOutputFormat,
SymbolTypeFilter, TdgOutputFormat,
};
use crate::services::simple_deep_context::{SimpleAnalysisConfig, SimpleDeepContext};
use anyhow::Result;
use std::path::PathBuf;
use tracing::{debug, info};
#[allow(clippy::too_many_arguments)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_analyze_deep_context(
project_path: PathBuf,
output: Option<PathBuf>,
format: DeepContextOutputFormat,
full: bool,
include: Vec<String>,
_exclude: Vec<String>,
period_days: u32,
_dag_type: Option<DagType>,
_max_depth: Option<usize>,
include_patterns: Vec<String>,
exclude_patterns: Vec<String>,
_cache_strategy: Option<String>,
_parallel: bool,
verbose: bool,
top_files: usize,
) -> Result<()> {
info!("🔍 Starting deep context analysis");
info!("📂 Project path: {}", project_path.display());
info!("📊 Analysis period: {} days", period_days);
let analyzer = SimpleDeepContext::new();
let mut include_features = include;
if full {
include_features.push("all".to_string());
}
let mut combined_exclude = exclude_patterns;
combined_exclude.extend([
"**/target/**".to_string(),
"**/node_modules/**".to_string(),
"**/.git/**".to_string(),
"**/build/**".to_string(),
"**/dist/**".to_string(),
"**/__pycache__/**".to_string(),
]);
let config = SimpleAnalysisConfig {
project_path: project_path.clone(),
include_features,
include_patterns,
exclude_patterns: combined_exclude,
enable_verbose: verbose,
};
if verbose {
debug!("Analysis configuration: {:?}", config);
}
let report = analyzer.analyze(config).await?;
let output_content = match (&format, &output) {
(DeepContextOutputFormat::Json, _) => analyzer.format_as_json(&report)?,
(DeepContextOutputFormat::Markdown, Some(_)) => {
analyzer.format_as_markdown(&report, top_files)
}
(DeepContextOutputFormat::Markdown, None) => {
format_deep_context_text(&report, top_files)
}
(DeepContextOutputFormat::Sarif, _) => {
analyzer.format_as_json(&report)?
}
};
if let Some(output_path) = output {
tokio::fs::write(&output_path, &output_content).await?;
info!(
"📄 Deep context analysis saved to: {}",
output_path.display()
);
} else {
println!("{output_content}");
}
info!("✅ Deep context analysis completed successfully");
info!(
"📊 Analyzed {} files in {:?}",
report.file_count, report.analysis_duration
);
info!(
"💡 Generated {} recommendations",
report.recommendations.len()
);
Ok(())
}
#[allow(clippy::too_many_arguments)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_analyze_tdg(
path: PathBuf,
threshold: Option<f64>,
top: Option<usize>,
format: TdgOutputFormat,
include_components: bool,
output: Option<PathBuf>,
critical_only: bool,
verbose: bool,
) -> Result<()> {
use super::new_tdg_handler::TdgAnalysisConfig;
let config = TdgAnalysisConfig {
path,
threshold,
top_files: top,
format,
include_components,
output,
critical_only,
verbose,
};
super::new_tdg_handler::handle_analyze_tdg(config).await
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_analyze_makefile(
path: PathBuf,
rules: Vec<String>,
format: MakefileOutputFormat,
fix: bool,
gnu_version: Option<String>,
top_files: usize,
) -> Result<()> {
super::super::analysis_utilities::handle_analyze_makefile(
path,
rules,
format,
fix,
gnu_version,
top_files,
)
.await
}
#[allow(clippy::too_many_arguments)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_analyze_defect_prediction(
project_path: PathBuf,
confidence_threshold: Option<f64>,
min_lines: Option<usize>,
include_low_confidence: bool,
format: DefectPredictionOutputFormat,
high_risk_only: bool,
include_recommendations: bool,
include: Vec<String>,
exclude: Vec<String>,
output: Option<PathBuf>,
perf: bool,
top_files: usize,
) -> Result<()> {
crate::cli::analysis::defect_prediction::handle_analyze_defect_prediction(
project_path,
confidence_threshold.unwrap_or(0.5) as f32,
min_lines.unwrap_or(100),
include_low_confidence,
format,
high_risk_only,
include_recommendations,
Some(include.join(",")),
Some(exclude.join(",")),
output,
perf,
top_files,
)
.await
}
#[allow(clippy::too_many_arguments)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_analyze_comprehensive(
project_path: PathBuf,
file: Option<PathBuf>,
files: Vec<PathBuf>,
format: ComprehensiveOutputFormat,
include_duplicates: bool,
include_dead_code: bool,
include_defects: bool,
include_complexity: bool,
include_tdg: bool,
confidence_threshold: f32,
min_lines: usize,
include: Option<String>,
exclude: Option<String>,
output: Option<PathBuf>,
perf: bool,
executive_summary: bool,
) -> Result<()> {
use super::comprehensive_analysis_handler::ComprehensiveAnalysisConfig;
let config = ComprehensiveAnalysisConfig {
project_path,
file,
files,
format,
include_duplicates,
include_dead_code,
include_defects,
include_complexity,
include_tdg,
confidence_threshold,
min_lines,
include,
exclude,
output,
perf,
executive_summary,
top_files: 20, };
super::comprehensive_analysis_handler::handle_analyze_comprehensive(config).await
}
#[allow(clippy::too_many_arguments)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_analyze_graph_metrics(
project_path: PathBuf,
metrics: Vec<GraphMetricType>,
pagerank_seeds: Vec<String>,
damping_factor: f32,
max_iterations: usize,
convergence_threshold: f64,
export_graphml: bool,
format: GraphMetricsOutputFormat,
include: Option<String>,
exclude: Option<String>,
output: Option<PathBuf>,
perf: bool,
top_k: usize,
min_centrality: f64,
) -> Result<()> {
crate::cli::analysis::graph_metrics::handle_analyze_graph_metrics(
project_path,
metrics,
pagerank_seeds,
damping_factor,
max_iterations,
convergence_threshold,
export_graphml,
format,
include,
exclude,
output,
perf,
top_k,
min_centrality,
)
.await
}
#[allow(clippy::too_many_arguments)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_analyze_symbol_table(
project_path: PathBuf,
format: SymbolTableOutputFormat,
filter: Option<SymbolTypeFilter>,
query: Option<String>,
include: Vec<String>,
exclude: Vec<String>,
show_unreferenced: bool,
show_references: bool,
output: Option<PathBuf>,
perf: bool,
) -> Result<()> {
crate::cli::analysis::symbol_table::handle_analyze_symbol_table(
project_path,
format,
filter,
query,
Some(include.join(",")),
Some(exclude.join(",")),
show_unreferenced,
show_references,
output,
perf,
)
.await
}
fn format_deep_context_text(
report: &crate::services::simple_deep_context::SimpleAnalysisReport,
top_files: usize,
) -> String {
use crate::cli::colors as c;
use std::fmt::Write;
let mut out = String::new();
let _ = writeln!(&mut out, "{}", c::header("Deep Context Analysis Report"));
let _ = writeln!(&mut out);
let _ = writeln!(&mut out, "{}\n", c::subheader("Summary"));
let _ = writeln!(
&mut out,
" Files Analyzed: {}",
c::number(&report.file_count.to_string())
);
let _ = writeln!(
&mut out,
" Analysis Duration: {}",
c::number(&format!("{:?}", report.analysis_duration))
);
let _ = writeln!(
&mut out,
" Total Functions: {}",
c::number(&report.complexity_metrics.total_functions.to_string())
);
let _ = writeln!(
&mut out,
" High Complexity Funcs: {}",
if report.complexity_metrics.high_complexity_count > 0 {
format!(
"{}{}{}",
c::YELLOW,
report.complexity_metrics.high_complexity_count,
c::RESET
)
} else {
c::number(&report.complexity_metrics.high_complexity_count.to_string())
}
);
let _ = writeln!(
&mut out,
" Average Complexity: {}\n",
c::number(&format!("{:.1}", report.complexity_metrics.avg_complexity))
);
if !report.file_complexity_details.is_empty() {
let _ = writeln!(&mut out, "{}\n", c::subheader("Top Files by Complexity"));
let mut sorted_files = report.file_complexity_details.clone();
sorted_files.sort_by(|a, b| {
b.complexity_score
.partial_cmp(&a.complexity_score)
.unwrap_or(std::cmp::Ordering::Equal)
});
let files_to_show = if top_files == 0 { 10 } else { top_files };
for (i, file_detail) in sorted_files.iter().take(files_to_show).enumerate() {
let filename = file_detail
.file_path
.file_name()
.and_then(|n| n.to_str())
.map_or_else(
|| file_detail.file_path.to_string_lossy().to_string(),
std::string::ToString::to_string,
);
let _ = writeln!(
&mut out,
" {}. {} - {} avg complexity ({} functions, {} high complexity)",
c::number(&(i + 1).to_string()),
c::path(&filename),
c::number(&format!("{:.1}", file_detail.avg_complexity)),
c::number(&file_detail.function_count.to_string()),
if file_detail.high_complexity_functions > 0 {
format!(
"{}{}{}",
c::YELLOW,
file_detail.high_complexity_functions,
c::RESET
)
} else {
c::number(&file_detail.high_complexity_functions.to_string())
},
);
}
let _ = writeln!(&mut out);
}
let _ = writeln!(&mut out, "{}\n", c::subheader("Recommendations"));
for (i, rec) in report.recommendations.iter().enumerate() {
let _ = writeln!(&mut out, " {}. {rec}", c::number(&(i + 1).to_string()));
}
out
}
#[cfg(test)]
#[path = "advanced_analysis_handlers_tests.rs"]
mod tests;