Skip to main content

allow_report/
diff_human.rs

1use crate::diff_policy_detail::policy_change_detail;
2use crate::diff_posture::{diff_net_posture, diff_posture_summary};
3use crate::{DiffFindingChange, DiffPolicyChange};
4
5pub fn render_diff_posture_summary_human(
6    current_failures: usize,
7    finding_changes: &[DiffFindingChange<'_>],
8    policy_changes: &[DiffPolicyChange<'_>],
9) -> String {
10    render_diff_posture_summary_human_with_evidence_health(
11        current_failures,
12        0,
13        0,
14        finding_changes,
15        policy_changes,
16    )
17}
18
19pub fn render_diff_posture_summary_human_with_evidence_health(
20    current_failures: usize,
21    broken_evidence_links: usize,
22    weak_evidence_references: usize,
23    finding_changes: &[DiffFindingChange<'_>],
24    policy_changes: &[DiffPolicyChange<'_>],
25) -> String {
26    let summary = diff_posture_summary(current_failures, finding_changes, policy_changes);
27    let posture = diff_net_posture(summary);
28    let mut out = String::new();
29    out.push_str("\nDiff posture summary:\n");
30    out.push_str(&format!("  net_posture: {}\n", posture.as_str()));
31    out.push_str(&format!(
32        "  reviewer_action: {}\n",
33        posture.reviewer_action()
34    ));
35    out.push_str(&format!(
36        "  current_check_failures: {}\n",
37        summary.current_failures
38    ));
39    if broken_evidence_links > 0 {
40        out.push_str(&format!(
41            "  broken_evidence_links: {broken_evidence_links}\n"
42        ));
43    }
44    if weak_evidence_references > 0 {
45        out.push_str(&format!(
46            "  weak_evidence_references: {weak_evidence_references}\n"
47        ));
48    }
49    out.push_str(&format!(
50        "  new_source_findings: {}\n",
51        summary.new_findings
52    ));
53    out.push_str(&format!(
54        "  removed_source_findings: {}\n",
55        summary.removed_findings
56    ));
57    out.push_str(&format!("  policy_failures: {}\n", summary.policy_failures));
58    out.push_str(&format!(
59        "  policy_review_items: {}\n",
60        summary.policy_review_items
61    ));
62    out.push_str(&format!(
63        "  policy_improvements: {}\n",
64        summary.policy_improvements
65    ));
66    out
67}
68
69pub fn render_diff_finding_changes_human(changes: &[DiffFindingChange<'_>]) -> String {
70    let mut out = String::new();
71    out.push_str("\nFinding posture changes:\n");
72    if changes.is_empty() {
73        out.push_str("  none\n");
74        return out;
75    }
76    for change in changes.iter().take(120) {
77        out.push_str(&format!(
78            "  {} {}{} at {}\n",
79            change.change,
80            change.kind,
81            change
82                .family
83                .map(|family| format!(".{family}"))
84                .unwrap_or_default(),
85            change.path
86        ));
87    }
88    if changes.len() > 120 {
89        out.push_str(&format!("  ... {} more omitted\n", changes.len() - 120));
90    }
91    out
92}
93
94pub fn render_diff_policy_changes_human(changes: &[DiffPolicyChange<'_>]) -> String {
95    let mut out = String::new();
96    out.push_str("\nPolicy posture changes:\n");
97    if changes.is_empty() {
98        out.push_str("  none\n");
99        return out;
100    }
101    for change in changes {
102        out.push_str(&format!(
103            "  {} {} {}: {}\n",
104            change.severity, change.allow_id, change.kind, change.message
105        ));
106        if let Some(detail) = policy_change_detail(change) {
107            out.push_str(&format!("    detail: {detail}\n"));
108        }
109    }
110    out
111}