use crate::contracts::DOCTOR_ARTIFACT;
use crate::json::{bool_json, option_json, push_json_fixed_artifact_preamble};
use crate::{CLAIM_BOUNDARY_TEXT, DoctorReport, InventoryContext};
use allow_core::json_escape;
pub fn render_doctor_human(facts: DoctorReport<'_>) -> String {
let mut out = String::new();
out.push_str(&format!("source tree root: {}\n", facts.source_tree_root));
out.push_str(&format!("root discovery: {}\n", facts.root_discovery));
match facts.config_path {
Some(path) => {
out.push_str(&format!("config: {path}\n"));
if let Some(schema_version) = facts.config_schema_version {
out.push_str(&format!("policy schema version: {schema_version}\n"));
}
if let Some(policy) = facts.config_policy {
out.push_str(&format!("policy: {policy}\n"));
}
if let Some(owner) = facts.config_owner {
out.push_str(&format!("policy owner: {owner}\n"));
}
if let Some(status) = facts.config_status {
out.push_str(&format!("policy status: {status}\n"));
}
out.push_str(&format!(
"config status: {}\n",
config_status_text(facts.config_valid, facts.config_diagnostic)
));
if let Some(count) = facts.broken_evidence_links {
out.push_str(&format!("broken evidence links: {count}\n"));
if count > 0 {
out.push_str(
"broken evidence worklist: cargo-allow worklist --item-kind broken_evidence_link --format json\n",
);
}
}
if let Some(count) = facts.weak_evidence_references {
out.push_str(&format!("weak evidence/link references: {count}\n"));
if count > 0 {
out.push_str(
"weak evidence worklist: cargo-allow worklist --item-kind weak_evidence_reference --format json\n",
);
}
}
}
None => out.push_str(&format!(
"config: not found; run `{}`\n",
suggested_init_command(facts.source_tree_root)
)),
}
out.push_str(&format!(
"inventory: source_tree/source_syntax via {}; files scanned: {}\n",
facts.inventory_source, facts.files_scanned
));
out.push_str(CLAIM_BOUNDARY_TEXT);
out
}
pub fn render_doctor_json(facts: DoctorReport<'_>) -> String {
let mut out = String::new();
out.push_str("{\n");
push_json_fixed_artifact_preamble(
&mut out,
DOCTOR_ARTIFACT,
InventoryContext::source_syntax(
facts.inventory_source,
Some(facts.source_tree_root),
Some(facts.files_scanned),
),
);
out.push_str(" \"root\": {\n");
out.push_str(&format!(
" \"path\": \"{}\",\n",
json_escape(facts.source_tree_root)
));
out.push_str(&format!(
" \"discovery\": \"{}\"\n",
json_escape(facts.root_discovery)
));
out.push_str(" },\n");
out.push_str(" \"config\": {\n");
out.push_str(&format!(
" \"found\": {},\n",
bool_json(facts.config_path.is_some())
));
out.push_str(&format!(
" \"path\": {},\n",
option_json(facts.config_path)
));
out.push_str(&format!(
" \"schema_version\": {},\n",
option_json(facts.config_schema_version)
));
out.push_str(&format!(
" \"policy\": {},\n",
option_json(facts.config_policy)
));
out.push_str(&format!(
" \"owner\": {},\n",
option_json(facts.config_owner)
));
out.push_str(&format!(
" \"status\": {},\n",
option_json(facts.config_status)
));
out.push_str(&format!(
" \"valid\": {},\n",
option_bool_json(facts.config_valid)
));
out.push_str(&format!(
" \"diagnostic\": {}",
option_json(facts.config_diagnostic)
));
if facts.config_path.is_none() {
out.push_str(&format!(
",\n \"suggested_init_command\": \"{}\"",
json_escape(&suggested_init_command(facts.source_tree_root))
));
}
if let Some(count) = facts.broken_evidence_links {
out.push_str(&format!(",\n \"broken_evidence_links\": {count}"));
}
if let Some(count) = facts.weak_evidence_references {
out.push_str(&format!(",\n \"weak_evidence_references\": {count}"));
}
out.push('\n');
out.push_str(" }\n");
out.push_str("}\n");
out
}
fn config_status_text(valid: Option<bool>, diagnostic: Option<&str>) -> String {
match (valid, diagnostic) {
(Some(true), _) => "valid".to_string(),
(Some(false), Some(diagnostic)) => format!("invalid: {diagnostic}"),
(Some(false), None) => "invalid".to_string(),
(None, _) => "not checked".to_string(),
}
}
fn option_bool_json(value: Option<bool>) -> &'static str {
match value {
Some(true) => "true",
Some(false) => "false",
None => "null",
}
}
fn suggested_init_command(source_tree_root: &str) -> String {
format!("cargo-allow init --root \"{source_tree_root}\"")
}