#![cfg_attr(coverage_nightly, coverage(off))]
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DebugAnalysis {
pub issue: String,
pub whys: Vec<WhyIteration>,
pub root_cause: Option<String>,
pub recommendations: Vec<Recommendation>,
pub evidence_summary: EvidenceSummary,
}
impl DebugAnalysis {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new(issue: String) -> Self {
Self {
issue,
whys: Vec::new(),
root_cause: None,
recommendations: Vec::new(),
evidence_summary: EvidenceSummary::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WhyIteration {
pub depth: u8,
pub question: String,
pub hypothesis: String,
pub evidence: Vec<Evidence>,
pub confidence: f64,
}
impl WhyIteration {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new(depth: u8, question: String, hypothesis: String) -> Self {
Self {
depth,
question,
hypothesis,
evidence: Vec::new(),
confidence: 0.5, }
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn with_confidence(mut self, confidence: f64) -> Self {
self.confidence = confidence.clamp(0.0, 1.0);
self
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn add_evidence(&mut self, evidence: Evidence) {
self.evidence.push(evidence);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Evidence {
pub source: EvidenceSource,
pub file: PathBuf,
pub metric: String,
pub value: serde_json::Value,
pub interpretation: String,
}
impl Evidence {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn new(
source: EvidenceSource,
file: PathBuf,
metric: String,
value: serde_json::Value,
interpretation: String,
) -> Self {
Self {
source,
file,
metric,
value,
interpretation,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum EvidenceSource {
Complexity,
SATD,
DeadCode,
GitChurn,
TDG,
ManualInspection,
EvoScoreTrajectory,
CoverageDelta,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Recommendation {
pub priority: Priority,
pub action: String,
pub file: Option<PathBuf>,
}
impl Recommendation {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn new(priority: Priority, action: String, file: Option<PathBuf>) -> Self {
Self {
priority,
action,
file,
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn high(action: String, file: Option<PathBuf>) -> Self {
Self::new(Priority::High, action, file)
}
pub fn medium(action: String, file: Option<PathBuf>) -> Self {
Self::new(Priority::Medium, action, file)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn low(action: String, file: Option<PathBuf>) -> Self {
Self::new(Priority::Low, action, file)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Priority {
High,
Medium,
Low,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EvidenceSummary {
pub complexity_violations: usize,
pub satd_markers: usize,
pub tdg_score: f64,
pub git_churn_high: bool,
#[serde(default)]
pub evoscore_trajectory: f64,
#[serde(default)]
pub coverage_delta: f64,
}
impl EvidenceSummary {
fn process_complexity_evidence(&mut self, evidence: &Evidence) {
let value = evidence
.value
.get("value")
.and_then(|v| v.as_f64())
.unwrap_or(0.0);
let threshold = evidence
.value
.get("threshold")
.and_then(|t| t.as_f64())
.unwrap_or(20.0);
if value > threshold {
self.complexity_violations += 1;
}
}
fn process_satd_evidence(&mut self, evidence: &Evidence) {
self.satd_markers += evidence
.value
.get("count")
.and_then(|c| c.as_u64())
.unwrap_or(1) as usize;
}
fn process_evidence(&mut self, evidence: &Evidence) {
match evidence.source {
EvidenceSource::Complexity => self.process_complexity_evidence(evidence),
EvidenceSource::SATD => self.process_satd_evidence(evidence),
EvidenceSource::TDG => {
if let Some(score) = evidence.value.as_f64() {
self.tdg_score = score;
}
}
EvidenceSource::GitChurn => {
let commits = evidence
.value
.get("commit_count")
.and_then(|c| c.as_u64())
.unwrap_or(0);
if commits > 10 {
self.git_churn_high = true;
}
}
EvidenceSource::EvoScoreTrajectory => {
self.evoscore_trajectory = evidence
.value
.get("evoscore")
.and_then(|v| v.as_f64())
.unwrap_or(0.0);
}
EvidenceSource::CoverageDelta => {
self.coverage_delta = evidence
.value
.get("delta")
.and_then(|v| v.as_f64())
.unwrap_or(0.0);
}
_ => {}
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn from_whys(whys: &[WhyIteration]) -> Self {
let mut summary = Self::default();
for why in whys {
for evidence in &why.evidence {
summary.process_evidence(evidence);
}
}
summary
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
include!("debug_analysis_tests.rs");
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod coverage_tests {
use super::*;
use proptest::prelude::*;
include!("debug_analysis_coverage_helpers.rs");
include!("debug_analysis_coverage_unit.rs");
include!("debug_analysis_coverage_recommend.rs");
include!("debug_analysis_coverage_props.rs");
}