//! JSON-formatted output for verification reports. use crate::report::object_value_to_json; use crate::verify::{AuditVerdict, VerifyReport}; /// Format a verification report as JSON. pub fn format_verify_json(report: &VerifyReport, show_unclaimed: bool) -> String { let results: Vec = report .results .iter() .filter(|r| r.verdict != AuditVerdict::Unclaimed || show_unclaimed) .map(|r| { let observations: Vec = r .matching_observations .iter() .map(|o| { serde_json::json!({ "concept_path": o.concept_path, "predicate": o.predicate, "value": object_value_to_json(&o.value), "file": o.file, "line": o.line, "confidence": o.confidence, }) }) .collect(); if let Some(ref claim) = r.claim { serde_json::json!({ "claim_id": claim.id, "concept_path": claim.concept_path, "predicate": claim.predicate, "verdict": r.verdict, "comparison": claim.comparison.to_string(), "explanation": r.explanation, "matching_observations": observations, }) } else { // Unclaimed observation — no backing claim let obs = r.matching_observations.first(); serde_json::json!({ "claim_id": serde_json::Value::Null, "concept_path": obs.map(|o| o.concept_path.as_str()).unwrap_or(""), "predicate": obs.map(|o| o.predicate.as_str()).unwrap_or(""), "verdict": r.verdict, "comparison": serde_json::Value::Null, "explanation": r.explanation, "matching_observations": observations, }) } }) .collect(); let output = serde_json::json!({ "summary": { "total_claims": report.summary.total_claims, "pass": report.summary.pass, "conflict": report.summary.conflict, "missing": report.summary.missing, "unclaimed": report.summary.unclaimed, }, "results": results, }); serde_json::to_string_pretty(&output).unwrap_or_else(|_| "{}".to_string()) } #[cfg(test)] mod tests { use super::*; use crate::verify::{VerifyReport, VerifySummary}; #[test] fn test_empty_report_json() { let report = VerifyReport { results: vec![], summary: VerifySummary::default() }; let output = format_verify_json(&report, false); let parsed: serde_json::Value = serde_json::from_str(&output).expect("valid json"); assert_eq!(parsed["summary"]["total_claims"], 0); assert_eq!(parsed["results"].as_array().map(|a| a.len()), Some(0)); } }