#![cfg_attr(coverage_nightly, coverage(off))]
use super::baseline::create_baseline;
use super::display::{display_gate_result, display_gate_result_table};
use super::parse_grade;
use super::TdgCommandConfig;
use crate::tdg::TdgAnalyzer;
use anyhow::Result;
use std::path::{Path, PathBuf};
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn execute_tdg_analysis<'a>(
analyzer: &'a TdgAnalyzer,
config: &'a TdgCommandConfig,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<crate::tdg::TdgScore>> + 'a>> {
Box::pin(async move {
if config.path.is_dir() {
Ok(analyzer.analyze_project(&config.path).await?.average())
} else {
analyzer.analyze_file(&config.path).await
}
})
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn validate_minimum_grade(
score: &crate::tdg::TdgScore,
config: &TdgCommandConfig,
) -> Result<()> {
if let Some(min_grade_str) = &config.min_grade {
let min_grade = parse_grade(min_grade_str)?;
if score.grade < min_grade {
return Err(anyhow::anyhow!(
"Grade {} is below minimum required grade {}",
super::format_grade(score.grade),
super::format_grade(min_grade)
));
}
}
Ok(())
}
pub(super) async fn handle_check_regression(
analyzer: &TdgAnalyzer,
baseline_path: &Path,
current_path: &Path,
format: crate::cli::TdgOutputFormat,
fail_on_regression: bool,
max_score_drop: Option<f32>,
allow_grade_drop: bool,
) -> Result<()> {
use crate::tdg::{GateConfig, QualityGate, RegressionGate, TdgBaseline};
println!("🔍 Checking for quality regressions...");
let baseline = TdgBaseline::load(baseline_path)?;
println!(
" ✅ Loaded baseline: {} files",
baseline.summary.total_files
);
let temp_output = std::env::temp_dir().join("pmat-regression-check.json");
create_baseline(analyzer, current_path, &temp_output, false).await?;
let current = TdgBaseline::load(&temp_output)?;
std::fs::remove_file(&temp_output).ok();
let mut config = GateConfig::default();
if let Some(drop) = max_score_drop {
config.max_score_drop = drop;
}
config.allow_grade_drop = allow_grade_drop;
let gate = RegressionGate::new(config);
let result = gate.check(&baseline, ¤t)?;
match &format {
crate::cli::TdgOutputFormat::Table => display_gate_result_table(&result),
crate::cli::TdgOutputFormat::Json => {
println!("{}", serde_json::to_string_pretty(&result)?);
}
crate::cli::TdgOutputFormat::Sarif => {
println!("SARIF format not yet implemented for quality gates");
}
crate::cli::TdgOutputFormat::Markdown => {
println!("Markdown format not yet implemented for quality gates");
}
}
if fail_on_regression && !result.passed {
return Err(anyhow::anyhow!("Quality regression detected"));
}
Ok(())
}
fn run_primary_gate(
new_files_only: bool,
min_grade_str: Option<&str>,
baseline_path: Option<&PathBuf>,
current: &crate::tdg::TdgBaseline,
) -> Result<crate::tdg::GateResult> {
use crate::tdg::{GateConfig, MinimumGradeGate, NewFileGate, QualityGate, TdgBaseline};
if new_files_only {
let baseline_path = baseline_path
.ok_or_else(|| anyhow::anyhow!("Baseline required for --new-files-only mode"))?;
let baseline = TdgBaseline::load(baseline_path)?;
let mut config = GateConfig::default();
if let Some(grade_str) = min_grade_str {
config.new_file_min_grade = parse_grade(grade_str)?;
}
NewFileGate::new(config).check(&baseline, current)
} else {
let baseline = TdgBaseline::new(None);
let mut config = GateConfig::default();
if let Some(grade_str) = min_grade_str {
config.default_min_grade = parse_grade(grade_str)?;
}
MinimumGradeGate::new(config).check(&baseline, current)
}
}
pub(super) async fn handle_check_quality(
analyzer: &TdgAnalyzer,
path: &Path,
min_grade_str: Option<&str>,
format: crate::cli::TdgOutputFormat,
fail_on_violation: bool,
new_files_only: bool,
baseline_path: Option<&PathBuf>,
) -> Result<()> {
use crate::tdg::{FGradeGate, QualityGate, TdgBaseline};
println!("🔍 Checking quality thresholds...");
let temp_output = std::env::temp_dir().join("pmat-quality-check.json");
create_baseline(analyzer, path, &temp_output, false).await?;
let current = TdgBaseline::load(&temp_output)?;
std::fs::remove_file(&temp_output).ok();
let f_grade_result = FGradeGate::with_defaults().check(&TdgBaseline::new(None), ¤t)?;
let result = run_primary_gate(new_files_only, min_grade_str, baseline_path, ¤t)?;
if !f_grade_result.violations.is_empty() {
println!("\n⚠️ F-Grade Warning: {}", f_grade_result.message);
println!(" F-grades cap project score at B regardless of average.");
display_gate_result(&f_grade_result, &format)?;
println!();
}
display_gate_result(&result, &format)?;
if fail_on_violation && (!result.passed || !f_grade_result.passed) {
return Err(anyhow::anyhow!("Quality violations detected"));
}
Ok(())
}