1use crate::contracts::PROPOSE_ARTIFACT;
2use crate::json::{bool_json, option_json, push_json_fixed_artifact_preamble};
3use crate::{CLAIM_BOUNDARY_TEXT, ProposeReport};
4use allow_core::json_escape;
5
6pub fn render_propose_human(report: ProposeReport<'_>) -> String {
7 let mut out = String::new();
8 out.push_str("cargo-allow propose summary\n");
9 out.push_str(&format!(
10 "inventory: {}/{} via {}{}\n",
11 report.inventory.scope,
12 report.inventory.scanner,
13 report.inventory.source,
14 propose_inventory_files_suffix(report.inventory)
15 ));
16 if let Some(root) = report.inventory.root {
17 out.push_str(&format!("source_tree_root: {root}\n"));
18 }
19 if let Some(kind) = report.kind {
20 out.push_str(&format!("kind filter: {kind}\n"));
21 }
22 out.push_str(&format!("findings scanned: {}\n", report.findings_scanned));
23 out.push_str(&format!(
24 "baseline_debt entries proposed: {}\n",
25 report.baseline_debt_entries_proposed
26 ));
27 out.push_str(&format!(
28 "unsafe baseline_debt entries proposed: {}\n",
29 report.unsafe_baseline_debt_entries_proposed
30 ));
31 out.push_str("owner: unowned\n");
32 out.push_str("classification: baseline_debt\n");
33 out.push_str("reason: Generated by cargo-allow propose; requires human review.\n");
34 out.push_str(&format!("expires: {}\n", report.expires));
35 if let Some(output) = report.policy_output {
36 out.push_str(&format!("output: {output}\n"));
37 } else {
38 out.push_str("output: stdout\n");
39 }
40 out.push_str(
41 "claim boundary: proposal only; generated debt still requires human review and evidence.\n",
42 );
43 out.push_str(CLAIM_BOUNDARY_TEXT);
44 out.push('\n');
45 out
46}
47
48fn propose_inventory_files_suffix(inventory: crate::InventoryContext<'_>) -> String {
49 inventory
50 .files_scanned
51 .map(|files| format!("; files scanned: {files}"))
52 .unwrap_or_default()
53}
54
55pub fn render_propose_json(report: ProposeReport<'_>) -> String {
56 let mut out = String::new();
57 out.push_str("{\n");
58 push_json_fixed_artifact_preamble(&mut out, PROPOSE_ARTIFACT, report.inventory);
59 out.push_str(" \"options\": {\n");
60 out.push_str(&format!(" \"kind\": {},\n", option_json(report.kind)));
61 out.push_str(&format!(
62 " \"expires\": \"{}\",\n",
63 json_escape(report.expires)
64 ));
65 out.push_str(&format!(
66 " \"policy_output\": {},\n",
67 option_json(report.policy_output)
68 ));
69 out.push_str(&format!(" \"force\": {}\n", bool_json(report.force)));
70 out.push_str(" },\n");
71 out.push_str(" \"summary\": {\n");
72 out.push_str(&format!(
73 " \"findings_scanned\": {},\n",
74 report.findings_scanned
75 ));
76 out.push_str(&format!(
77 " \"baseline_debt_entries_proposed\": {},\n",
78 report.baseline_debt_entries_proposed
79 ));
80 out.push_str(&format!(
81 " \"unsafe_baseline_debt_entries_proposed\": {}\n",
82 report.unsafe_baseline_debt_entries_proposed
83 ));
84 out.push_str(" },\n");
85 out.push_str(" \"generated_entry_defaults\": {\n");
86 out.push_str(" \"owner\": \"unowned\",\n");
87 out.push_str(" \"classification\": \"baseline_debt\",\n");
88 out.push_str(" \"reason\": \"Generated by cargo-allow propose; requires human review.\",\n");
89 out.push_str(&format!(
90 " \"expires\": \"{}\"\n",
91 json_escape(report.expires)
92 ));
93 out.push_str(" }\n");
94 out.push_str("}\n");
95 out
96}