use prikk_store::{BundlePreviewConflict, BundlePreviewReport};
use crate::stdout::println;
use super::verification::escape_json_string;
const RENAME_HONESTY_NOTE: &str = "a moved path is not yet distinguishable from a delete plus a \
create in this preview -- until rename authoring lands, do not infer that any delete+create \
pair shown here was a move";
pub(crate) fn print_bundle_preview_json(report: &BundlePreviewReport) {
let mut json = String::new();
json.push_str("{\n");
json.push_str(" \"schema_version\": \"bundle-preview-v1\",\n");
json.push_str(" \"bundle_ref\": ");
json.push_str(&escape_json_string(&report.bundle_ref_name));
json.push_str(",\n \"local_ref\": ");
json.push_str(&escape_json_string(&report.local_ref_name));
json.push_str(",\n \"connectivity\": ");
json.push_str(&escape_json_string(report.connectivity.as_str()));
json.push_str(",\n \"conflict\": ");
match &report.conflict {
Some(conflict) => json.push_str(&escape_json_string(conflict.as_str())),
None => json.push_str("null"),
}
json.push_str(",\n \"conflict_detail\": ");
match &report.conflict {
Some(BundlePreviewConflict::Conflict { detail }) => {
json.push_str(&escape_json_string(detail));
}
Some(BundlePreviewConflict::Undetermined { reason }) => {
json.push_str(&escape_json_string(reason));
}
_ => json.push_str("null"),
}
json.push_str(",\n \"sealed_by\": [");
for (index, key_id) in report.sealed_by.iter().enumerate() {
if index > 0 {
json.push(',');
}
json.push_str(&escape_json_string(key_id));
}
json.push_str("],\n \"sealed_by_note\": ");
json.push_str(&escape_json_string(
"recorded signer identity, continuity only -- not a trust decision",
));
json.push_str(",\n \"effects\": [");
for (index, effect) in report.effects.iter().enumerate() {
if index > 0 {
json.push(',');
}
json.push_str("\n {\"path\": ");
json.push_str(&escape_json_string(&effect.path));
json.push_str(", \"kind\": ");
json.push_str(&escape_json_string(effect.kind.as_str()));
json.push_str(", \"current_bytes\": ");
push_optional_u64(&mut json, effect.current_bytes);
json.push_str(", \"after_bytes\": ");
push_optional_u64(&mut json, effect.after_bytes);
json.push('}');
}
if !report.effects.is_empty() {
json.push_str("\n ");
}
json.push_str("],\n \"rename_note\": ");
json.push_str(&escape_json_string(RENAME_HONESTY_NOTE));
json.push_str(",\n \"manifest\": ");
match &report.manifest {
Some(manifest) => {
json.push_str("{\"repository_format\": ");
json.push_str(&manifest.repository_format.to_string());
json.push_str(", \"tool_version\": ");
json.push_str(&escape_json_string(&manifest.tool_version));
json.push('}');
}
None => json.push_str("null"),
}
json.push_str("\n}");
println!("{json}");
}
fn push_optional_u64(json: &mut String, value: Option<u64>) {
match value {
Some(value) => json.push_str(&value.to_string()),
None => json.push_str("null"),
}
}
pub(crate) fn print_bundle_preview_plain(report: &BundlePreviewReport) {
println!("bundle: {}", report.bundle_ref_name);
println!("previewed against: {}", report.local_ref_name);
println!("connectivity: {}", report.connectivity.as_str());
match &report.conflict {
Some(BundlePreviewConflict::AppliesCleanly) => println!("conflict: none, applies cleanly"),
Some(BundlePreviewConflict::Conflict { detail }) => {
println!("conflict: yes -- {detail}");
}
Some(BundlePreviewConflict::Undetermined { reason }) => {
println!("conflict: undetermined -- {reason}");
}
None => {}
}
if report.sealed_by.is_empty() {
println!("sealed by: (no MAINTAINER signature found on the bundle's own RefState)");
} else {
println!(
"sealed by: {} (continuity only, not a trust decision)",
report.sealed_by.join(", ")
);
}
println!("effects: {}", report.effects.len());
for effect in &report.effects {
println!(" {} {}", effect.kind.as_str(), effect.path);
}
println!("note: {RENAME_HONESTY_NOTE}");
}