fn banner_enabled(format: OracleOutputFormat) -> bool {
format != OracleOutputFormat::Json
}
fn notify_results_written(output_path: &Path, format: OracleOutputFormat) {
if banner_enabled(format) {
crate::status_println!("✅ Results written to: {}", output_path.display());
} else {
crate::status_eprintln!("✅ Results written to: {}", output_path.display());
}
}
async fn handle_oracle_fix(
path: &Path,
max_iterations: usize,
auto_apply_threshold: f32,
review_threshold: f32,
dry_run: bool,
format: OracleOutputFormat,
output: Option<&Path>,
) -> Result<()> {
if banner_enabled(format) {
crate::status_println!("🔮 PMAT Oracle - PDCA Quality Improvement Loop");
crate::status_println!(" Path: {}", path.display());
crate::status_println!(" Max iterations: {}", max_iterations);
crate::status_println!(
" Thresholds: auto={:.2}, review={:.2}",
auto_apply_threshold,
review_threshold
);
if dry_run {
crate::status_println!(" Mode: DRY RUN (no changes will be applied)");
}
crate::status_println!();
}
if !path.exists() {
anyhow::bail!("Project path does not exist: {}", path.display());
}
let config = OracleConfig {
max_iterations,
auto_apply_threshold,
review_threshold,
..Default::default()
};
let targets = ConvergenceTargets::default();
let pdca = PdcaLoop::with_config(config, targets.clone());
if dry_run {
if banner_enabled(format) {
crate::status_println!("🔍 Dry run: Collecting signals only...\n");
}
let results = pdca.run_iterations(path, 1).await?;
if let Some(result) = results.first() {
format_iteration_result(result, &format, output)?;
}
} else {
if banner_enabled(format) {
crate::status_println!("🚀 Starting PDCA loop...\n");
}
let results = pdca.run(path).await?;
let formatted = format_pdca_results(&results, &targets, format)?;
if let Some(output_path) = output {
std::fs::write(output_path, &formatted)?;
notify_results_written(output_path, format);
} else {
println!("{}", formatted);
}
}
Ok(())
}
async fn handle_oracle_status(path: &Path, format: OracleOutputFormat) -> Result<()> {
if banner_enabled(format) {
crate::status_println!("📊 PMAT Oracle - Project Quality Status");
crate::status_println!(" Path: {}", path.display());
crate::status_println!();
}
if !path.exists() {
anyhow::bail!("Project path does not exist: {}", path.display());
}
let targets = ConvergenceTargets::default();
if banner_enabled(format) {
crate::status_println!(" Collecting signals (cargo build / clippy / test)...");
crate::status_println!();
}
let collected = collect_project_metrics(path).await?;
let status = convergence_status_with_gaps(&targets, &collected);
let formatted = format_status(
&collected.metrics,
&targets,
&status,
&collected.unmeasured,
format,
)?;
println!("{}", formatted);
Ok(())
}
struct CollectedMetrics {
metrics: ProjectMetrics,
unmeasured: Vec<&'static str>,
}
fn convergence_status_with_gaps(
targets: &ConvergenceTargets,
collected: &CollectedMetrics,
) -> crate::services::oracle::ConvergenceStatus {
use crate::services::oracle::ConvergenceStatus;
let checked = targets.check(&collected.metrics);
if collected.unmeasured.is_empty() {
return checked;
}
let mut remaining = match checked {
ConvergenceStatus::Converged => Vec::new(),
ConvergenceStatus::NotConverged { remaining } => remaining,
};
for gap in &collected.unmeasured {
remaining.push(format!("{}: not measured", gap));
}
ConvergenceStatus::NotConverged { remaining }
}
async fn collect_project_metrics(path: &Path) -> Result<CollectedMetrics> {
use crate::services::oracle::{AggregatedCollector, SignalSource};
let mut metrics = ProjectMetrics::default();
let mut unmeasured = vec![
"test coverage",
"mutation score",
"TDG score",
"rust project score",
"SATD markers",
"dead code",
"complexity",
];
if !path.join("Cargo.toml").exists() {
unmeasured.push("compiler errors");
unmeasured.push("clippy warnings");
unmeasured.push("test failures");
return Ok(CollectedMetrics {
metrics,
unmeasured,
});
}
let signals = AggregatedCollector::new().collect_all(path).await?;
metrics.compiler_errors = signals
.iter()
.filter(|s| s.source == SignalSource::Rustc)
.count();
metrics.clippy_warnings = signals
.iter()
.filter(|s| s.source == SignalSource::Clippy)
.count();
metrics.test_failures = signals
.iter()
.filter(|s| s.source == SignalSource::CargoTest)
.count();
Ok(CollectedMetrics {
metrics,
unmeasured,
})
}
#[cfg(test)]
mod oracle_status_metric_tests {
use super::*;
#[tokio::test]
async fn test_collect_project_metrics_reports_gaps_without_a_manifest() {
let dir = tempfile::TempDir::new().unwrap();
let collected = collect_project_metrics(dir.path()).await.unwrap();
assert!(
collected.unmeasured.contains(&"compiler errors"),
"cargo cannot run without a manifest: {:?}",
collected.unmeasured
);
assert!(collected.unmeasured.contains(&"test coverage"));
}
#[test]
fn test_unmeasured_targets_block_convergence() {
use crate::services::oracle::ConvergenceStatus;
let targets = ConvergenceTargets::default();
let metrics = ProjectMetrics {
test_coverage: 1.0,
mutation_score: 1.0,
tdg_score: 100.0,
rust_project_score: 100,
..Default::default()
};
let all_measured = CollectedMetrics {
metrics: metrics.clone(),
unmeasured: Vec::new(),
};
assert!(matches!(
convergence_status_with_gaps(&targets, &all_measured),
ConvergenceStatus::Converged
));
let with_gap = CollectedMetrics {
metrics,
unmeasured: vec!["TDG score"],
};
match convergence_status_with_gaps(&targets, &with_gap) {
ConvergenceStatus::Converged => {
panic!("must not declare convergence over a target nothing measured")
}
ConvergenceStatus::NotConverged { remaining } => {
assert!(remaining
.iter()
.any(|r| r.contains("TDG score: not measured")));
}
}
}
}
async fn handle_oracle_single(
path: &Path,
format: OracleOutputFormat,
output: Option<&Path>,
) -> Result<()> {
if banner_enabled(format) {
crate::status_println!("⚡ PMAT Oracle - Single PDCA Iteration");
crate::status_println!(" Path: {}", path.display());
crate::status_println!();
}
if !path.exists() {
anyhow::bail!("Project path does not exist: {}", path.display());
}
let pdca = PdcaLoop::new();
let result = pdca.run_single(path).await?;
let formatted = format_single_result(&result, format)?;
if let Some(output_path) = output {
std::fs::write(output_path, &formatted)?;
notify_results_written(output_path, format);
} else {
println!("{}", formatted);
}
Ok(())
}