allow_report/
diff_human.rs1use 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}