mod analysis;
mod output;
mod thresholds;
pub mod types;
pub use types::{ValidateConfig, ValidationDetails};
use crate::commands::analyze;
use crate::core::{AnalysisResults, Language};
use crate::formatting::FormattingConfig;
use crate::io;
use crate::progress::{ProgressConfig, ProgressManager};
use crate::utils::risk_analyzer;
use crate::{config, risk};
use analysis::{
calculate_unified_analysis, read_parallel_options_from_env, ValidationAnalysisOptions,
};
use anyhow::Result;
use output::{
display_timing_information, generate_report_if_requested, print_parallel_status,
print_validation_failure, print_validation_success, warn_deprecated_thresholds,
};
use thresholds::{find_deprecated_thresholds, validate_basic, validate_with_risk};
pub fn validate_project(config: ValidateConfig) -> Result<()> {
let complexity_threshold = 10;
let duplication_threshold = 50;
let parallel_enabled = !config.no_parallel;
let jobs = config.jobs;
setup_parallel_processing(parallel_enabled, jobs, config.verbosity);
setup_progress_manager(config.verbosity);
let results = analyze::analyze_project(
config.path.clone(),
vec![Language::Rust, Language::Python],
complexity_threshold,
duplication_threshold,
parallel_enabled,
FormattingConfig::default(),
)?;
let risk_insights = get_risk_insights(&config, &results)?;
generate_report_if_requested(&config, &results, &risk_insights)?;
validate_and_report(&config, &results, &risk_insights)
}
fn setup_parallel_processing(enabled: bool, jobs: usize, verbosity: u8) {
if enabled {
std::env::set_var("DEBTMAP_PARALLEL", "true");
print_parallel_status(enabled, jobs, verbosity);
}
if jobs > 0 {
std::env::set_var("DEBTMAP_JOBS", jobs.to_string());
}
}
fn setup_progress_manager(verbosity: u8) {
let quiet = std::env::var("DEBTMAP_QUIET").is_ok();
let progress_config = ProgressConfig::from_env(quiet, verbosity);
ProgressManager::init_global(progress_config);
if let Some(manager) = ProgressManager::global() {
manager.tui_start_stage(0);
}
}
fn cleanup_progress() {
if let Some(manager) = ProgressManager::global() {
manager.tui_set_progress(1.0);
manager.tui_cleanup();
}
io::progress::AnalysisProgress::with_global(|p| p.finish());
}
fn get_risk_insights(
config: &ValidateConfig,
results: &AnalysisResults,
) -> Result<Option<risk::RiskInsight>> {
match (&config.coverage_file, config.enable_context) {
(Some(lcov_path), _) => risk_analyzer::analyze_risk_with_coverage(
results,
lcov_path,
&config.path,
config.enable_context,
config.context_providers.clone(),
config.disable_context.clone(),
),
(None, true) => risk_analyzer::analyze_risk_without_coverage(
results,
config.enable_context,
config.context_providers.clone(),
config.disable_context.clone(),
&config.path,
),
_ => Ok(None),
}
}
fn validate_and_report(
config: &ValidateConfig,
results: &AnalysisResults,
risk_insights: &Option<risk::RiskInsight>,
) -> Result<()> {
let lcov_data = config
.coverage_file
.as_ref()
.and_then(|path| risk::lcov::parse_lcov_file(path).ok());
let validation_thresholds = config::get_validation_thresholds();
let deprecated = find_deprecated_thresholds(&validation_thresholds);
warn_deprecated_thresholds(&deprecated);
let parallel_options = read_parallel_options_from_env();
let options = ValidationAnalysisOptions {
parallel: parallel_options.parallel,
jobs: parallel_options.jobs,
enable_context: config.enable_context,
context_providers: config.context_providers.clone(),
disable_context: config.disable_context.clone(),
};
let unified = calculate_unified_analysis(results, config.coverage_file.as_ref(), &options);
cleanup_progress();
display_timing_information(&unified, config.verbosity);
let total_debt_score = unified.total_debt_score as u32;
let debt_density = unified.debt_density;
let (pass, details) = perform_validation(
results,
risk_insights,
lcov_data.as_ref(),
total_debt_score,
debt_density,
&validation_thresholds,
config.max_debt_density,
);
if pass {
print_validation_success(&details, config.verbosity);
Ok(())
} else {
print_validation_failure(&details, risk_insights, config.verbosity);
anyhow::bail!("Validation failed")
}
}
fn perform_validation(
results: &AnalysisResults,
risk_insights: &Option<risk::RiskInsight>,
lcov_data: Option<&risk::lcov::LcovData>,
total_debt_score: u32,
debt_density: f64,
thresholds: &config::ValidationThresholds,
max_debt_density_override: Option<f64>,
) -> (bool, ValidationDetails) {
match risk_insights {
Some(insights) => {
let coverage_percentage = lcov_data
.map(|lcov| lcov.get_overall_coverage())
.unwrap_or(0.0);
validate_with_risk(
results,
insights,
coverage_percentage,
total_debt_score,
debt_density,
thresholds,
max_debt_density_override,
)
}
None => validate_basic(
results,
total_debt_score,
debt_density,
thresholds,
max_debt_density_override,
),
}
}
#[cfg(test)]
mod tests;