mod api;
mod architecture;
mod coverage;
mod dead_code;
mod duplicates;
mod history;
mod size;
mod topology;
use std::collections::{BTreeMap, BTreeSet};
use serde_json::Value;
use crate::debt::{DebtBaseline, DebtDelta, classify_debt};
use crate::weavatrix::IntelligenceError;
use wvq_domain::{CheckId, FindingState, QualityFinding, Severity};
pub use api::map_api_delta;
pub use architecture::map_architecture_violation;
pub use coverage::{
CoverageMeasurement, NodeCoverage, map_coverage_findings, map_coverage_to_nodes,
};
pub use dead_code::{dead_ids, live_ids, map_dead_code_report};
pub use duplicates::{family_sizes, map_duplicates_report};
pub use history::map_history_risk;
pub use size::size_growth_findings;
pub use topology::map_topology_delta;
pub fn gate_architecture(
base: &Value,
head: &Value,
baseline: &DebtBaseline,
) -> Result<DebtDelta, IntelligenceError> {
let base_findings = map_architecture_report(base)?;
let mut head_findings = map_architecture_report(head)?;
head_findings.extend(size_growth_findings(base, head)?);
Ok(classify_debt(&base_findings, &head_findings, baseline))
}
pub fn map_architecture_report(report: &Value) -> Result<Vec<QualityFinding>, IntelligenceError> {
if report.get("state").and_then(Value::as_str) == Some("NOT_CONFIGURED") {
return Ok(Vec::new());
}
let mut findings = Vec::new();
for key in ["new", "existing", "warnings", "excepted"] {
let Some(items) = report.get(key).and_then(Value::as_array) else {
continue;
};
for item in items {
if item.is_string() {
continue;
}
findings.push(map_architecture_violation(item)?);
}
}
Ok(findings)
}
pub fn gate_dead_code(
base: &Value,
head: &Value,
baseline: &DebtBaseline,
) -> Result<DebtDelta, IntelligenceError> {
let prior_dead = dead_ids(base);
let prior_live = live_ids(base);
let base_findings = map_dead_code_report(base, &BTreeSet::new(), &BTreeSet::new())?;
let head_findings = map_dead_code_report(head, &prior_dead, &prior_live)?;
Ok(relabel_returned(
classify_debt(&base_findings, &head_findings, baseline),
"WVQ-DEAD-",
"WVQ-DEAD-004",
Severity::Error,
))
}
pub fn gate_clones(
base: &Value,
head: &Value,
baseline: &DebtBaseline,
) -> Result<DebtDelta, IntelligenceError> {
let prior = family_sizes(base);
let base_findings = map_duplicates_report(base, &BTreeMap::new())?;
let head_findings = map_duplicates_report(head, &prior)?;
Ok(relabel_returned(
classify_debt(&base_findings, &head_findings, baseline),
"WVQ-CLONE-",
"WVQ-CLONE-005",
Severity::Error,
))
}
pub fn gate_api(
base: &Value,
head: &Value,
spec: &Value,
proofs: &Value,
baseline: &DebtBaseline,
) -> Result<DebtDelta, IntelligenceError> {
let findings = map_api_delta(base, head, spec, proofs)?;
Ok(classify_debt(&[], &findings, baseline))
}
pub fn gate_history(
report: &Value,
baseline: &DebtBaseline,
) -> Result<DebtDelta, IntelligenceError> {
let findings = map_history_risk(report)?;
Ok(classify_debt(&[], &findings, baseline))
}
pub fn gate_coverage(
base_cov: Option<&wvq_runtime::CoverageArtifact>,
head_cov: Option<&wvq_runtime::CoverageArtifact>,
base_graph: &Value,
head_graph: &Value,
baseline: &DebtBaseline,
) -> Result<DebtDelta, IntelligenceError> {
let findings = map_coverage_findings(base_cov, head_cov, base_graph, head_graph)?;
Ok(classify_debt(&[], &findings, baseline))
}
pub fn gate_topology(
base: &Value,
head: &Value,
baseline: &DebtBaseline,
) -> Result<DebtDelta, IntelligenceError> {
let findings = map_topology_delta(base, head)?;
Ok(classify_debt(&[], &findings, baseline))
}
fn relabel_returned(
mut delta: DebtDelta,
prefix: &str,
returned_id: &str,
severity: Severity,
) -> DebtDelta {
for finding in &mut delta.returned {
if finding.check.as_str().starts_with(prefix) {
finding.check = CheckId::new(returned_id).expect("static returned check id");
finding.severity = severity;
finding.state = FindingState::Returned;
}
}
delta
}