Skip to main content

allow_report/
propose.rs

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}