#[allow(clippy::too_many_arguments)]
pub async fn handle_analyze_provability(
project_path: PathBuf,
functions: Vec<String>,
_analysis_depth: usize,
format: ProvabilityOutputFormat,
high_confidence_only: bool,
include_evidence: bool,
output: Option<PathBuf>,
top_files: usize,
) -> Result<()> {
use crate::services::lightweight_provability_analyzer::LightweightProvabilityAnalyzer;
eprintln!("🔬 Analyzing function provability...");
let analyzer = LightweightProvabilityAnalyzer::new();
let function_ids = get_function_ids(&project_path, &functions).await?;
let summaries = analyzer.analyze_incrementally(&function_ids).await;
eprintln!("✅ Analyzed {} functions", summaries.len());
let filtered_summaries_owned = prepare_summaries(&summaries, high_confidence_only);
let content = format_provability_output(
format,
&function_ids,
&filtered_summaries_owned,
include_evidence,
top_files,
)?;
write_provability_output(output, &content).await?;
Ok(())
}
async fn get_function_ids(
project_path: &Path,
functions: &[String],
) -> Result<Vec<crate::services::lightweight_provability_analyzer::FunctionId>> {
use crate::cli::provability_helpers::{discover_project_functions, parse_function_spec};
if functions.is_empty() {
discover_project_functions(project_path).await
} else {
let mut ids = Vec::new();
for spec in functions {
ids.push(parse_function_spec(spec, project_path)?);
}
Ok(ids)
}
}
fn prepare_summaries(summaries: &[ProofSummary], high_confidence_only: bool) -> Vec<ProofSummary> {
use crate::cli::provability_helpers::filter_summaries;
let filtered_summaries = filter_summaries(summaries, high_confidence_only);
filtered_summaries.into_iter().cloned().collect()
}
fn format_provability_output(
format: ProvabilityOutputFormat,
function_ids: &[crate::services::lightweight_provability_analyzer::FunctionId],
summaries: &[ProofSummary],
include_evidence: bool,
top_files: usize,
) -> Result<String> {
use crate::cli::provability_helpers::{
format_provability_detailed, format_provability_json, format_provability_sarif,
format_provability_summary,
};
match format {
ProvabilityOutputFormat::Json => {
format_provability_json(function_ids, summaries, include_evidence)
}
ProvabilityOutputFormat::Summary => {
format_provability_summary(function_ids, summaries, top_files)
}
ProvabilityOutputFormat::Full | ProvabilityOutputFormat::Markdown => {
format_provability_detailed(function_ids, summaries, include_evidence)
}
ProvabilityOutputFormat::Sarif => format_provability_sarif(function_ids, summaries),
}
}
async fn write_provability_output(output: Option<PathBuf>, content: &str) -> Result<()> {
if let Some(output_path) = output {
tokio::fs::write(&output_path, content).await?;
eprintln!(
"✅ Provability analysis written to: {}",
output_path.display()
);
} else {
println!("{content}");
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub async fn handle_analyze_defect_prediction(
project_path: PathBuf,
confidence_threshold: f32,
_min_lines: usize,
include_low_confidence: bool,
format: DefectPredictionOutputFormat,
high_risk_only: bool,
_include_recommendations: bool,
_include: Option<String>,
_exclude: Option<String>,
output: Option<PathBuf>,
_perf: bool,
top_files: usize,
) -> Result<()> {
print_defect_analysis_header(
&project_path,
high_risk_only,
include_low_confidence,
&format,
);
let config = create_defect_config(
confidence_threshold,
_min_lines,
include_low_confidence,
high_risk_only,
_include_recommendations,
_include,
_exclude,
);
let predictions =
compute_defect_predictions(&project_path, &config, confidence_threshold).await?;
let top_predictions = filter_and_sort_predictions(predictions, top_files);
let report = create_defect_report_from_predictions(top_predictions)?;
let content = format_defect_report(&report, format)?;
output_defect_result(content, output).await?;
Ok(())
}
fn print_defect_analysis_header(
project_path: &Path,
high_risk_only: bool,
include_low_confidence: bool,
format: &DefectPredictionOutputFormat,
) {
eprintln!("🔮 Analyzing defect probability...");
eprintln!("📁 Project path: {}", project_path.display());
eprintln!("🎯 High risk only: {high_risk_only}");
eprintln!("📊 Include low confidence: {include_low_confidence}");
eprintln!("📄 Format: {format:?}");
}
fn create_defect_config(
confidence_threshold: f32,
min_lines: usize,
include_low_confidence: bool,
high_risk_only: bool,
include_recommendations: bool,
include: Option<String>,
exclude: Option<String>,
) -> crate::cli::defect_prediction_helpers::DefectPredictionConfig {
crate::cli::defect_prediction_helpers::DefectPredictionConfig {
confidence_threshold,
min_lines,
include_low_confidence,
high_risk_only,
include_recommendations,
include,
exclude,
}
}
async fn compute_defect_predictions(
project_path: &Path,
config: &crate::cli::defect_prediction_helpers::DefectPredictionConfig,
confidence_threshold: f32,
) -> Result<Vec<(String, crate::services::defect_probability::DefectScore)>> {
use crate::cli::defect_prediction_helpers::discover_source_files_for_defect_analysis;
use crate::services::defect_probability::DefectProbabilityCalculator;
let calculator = DefectProbabilityCalculator::new();
let files = discover_source_files_for_defect_analysis(project_path, config).await?;
let mut predictions = Vec::new();
for (file_path, _content, lines) in files {
let metrics = create_file_metrics(&file_path, lines);
let score = calculator.calculate(&metrics);
if should_include_prediction(
&score,
config.high_risk_only,
config.include_low_confidence,
confidence_threshold,
) {
predictions.push((file_path.to_string_lossy().to_string(), score));
}
}
Ok(predictions)
}
fn create_file_metrics(
file_path: &Path,
lines: usize,
) -> crate::services::defect_probability::FileMetrics {
crate::services::defect_probability::FileMetrics {
file_path: file_path.to_string_lossy().to_string(),
churn_score: 0.5, complexity: (lines as f32) * 0.1, duplicate_ratio: 0.1, afferent_coupling: 1.0,
efferent_coupling: 1.0,
lines_of_code: lines,
cyclomatic_complexity: (lines / 20) as u32, cognitive_complexity: (lines / 15) as u32, }
}
fn should_include_prediction(
score: &crate::services::defect_probability::DefectScore,
high_risk_only: bool,
include_low_confidence: bool,
confidence_threshold: f32,
) -> bool {
use crate::services::defect_probability::RiskLevel;
if high_risk_only && matches!(score.risk_level, RiskLevel::Low | RiskLevel::Medium) {
return false;
}
if !include_low_confidence && score.probability < confidence_threshold {
return false;
}
true
}
fn filter_and_sort_predictions(
mut predictions: Vec<(String, crate::services::defect_probability::DefectScore)>,
top_files: usize,
) -> Vec<(String, crate::services::defect_probability::DefectScore)> {
predictions.sort_unstable_by(|a, b| {
b.1.probability
.partial_cmp(&a.1.probability)
.unwrap_or(std::cmp::Ordering::Equal)
});
predictions.truncate(top_files);
predictions
}
fn format_defect_report(
report: &DefectPredictionReport,
format: DefectPredictionOutputFormat,
) -> Result<String> {
use DefectPredictionOutputFormat::{Csv, Detailed, Json, Sarif, Summary};
match format {
Summary => format_defect_summary(report, 10),
Json => serde_json::to_string_pretty(report).map_err(Into::into),
Detailed => format_defect_full(report, 10),
Sarif => format_defect_sarif(report),
Csv => format_defect_csv(report),
}
}
async fn output_defect_result(content: String, output: Option<PathBuf>) -> Result<()> {
eprintln!("✅ Defect prediction complete");
if let Some(output_path) = output {
tokio::fs::write(&output_path, &content).await?;
eprintln!("📝 Written to {}", output_path.display());
} else {
println!("{content}");
}
Ok(())
}