use bitcoin::bip32::ChildNumber;
use clap::Args;
use mk_codec::KeyCard;
use serde_json::json;
use crate::cmd::{classify_code_variant, fmt_fingerprint, fmt_stub, read_mk1_strings};
use crate::error::{CliError, Result};
#[derive(Args, Debug)]
pub struct InspectArgs {
pub mk1_strings: Vec<String>,
#[arg(long)]
pub json: bool,
}
pub fn run(args: InspectArgs) -> Result<()> {
let strings = read_mk1_strings(&args.mk1_strings)?;
let refs: Vec<&str> = strings.iter().map(|s| s.as_str()).collect();
let card = mk_codec::decode(&refs)?;
let per_chunk_variants: Vec<&str> = strings.iter().map(|s| classify_code_variant(s)).collect();
if args.json {
emit_json(&card, &strings, &per_chunk_variants)?;
} else {
emit_text(&card, &strings, &per_chunk_variants);
}
Ok(())
}
fn xpub_fingerprint_hex(card: &KeyCard) -> String {
fmt_fingerprint(&card.xpub.fingerprint())
}
fn path_components_text(card: &KeyCard) -> Vec<String> {
card.origin_path
.into_iter()
.map(|c| match c {
ChildNumber::Hardened { index } => format!("{index}h (hardened)"),
ChildNumber::Normal { index } => format!("{index} (normal)"),
})
.collect()
}
fn emit_text(card: &KeyCard, strings: &[String], variants: &[&str]) {
println!("xpub: {}", card.xpub);
match &card.origin_fingerprint {
Some(fp) => println!("origin_fingerprint: {}", fmt_fingerprint(fp)),
None => println!("origin_fingerprint: (omitted, privacy-preserving mode)"),
}
println!("xpub_fingerprint: {}", xpub_fingerprint_hex(card));
println!("origin_path: {}", card.origin_path);
for (i, comp) in path_components_text(card).into_iter().enumerate() {
println!(" component[{i}]: {comp}");
}
let stubs: Vec<String> = card.policy_id_stubs.iter().map(fmt_stub).collect();
println!("policy_id_stubs: {}", stubs.join(", "));
println!("chunks: {}", strings.len());
for (i, v) in variants.iter().enumerate() {
println!(" chunk[{i}]: {v} (BCH variant)");
}
}
fn emit_json(card: &KeyCard, strings: &[String], variants: &[&str]) -> Result<()> {
let stubs: Vec<String> = card.policy_id_stubs.iter().map(fmt_stub).collect();
let path_components = path_components_text(card);
let envelope = json!({
"schema_version": 1,
"xpub": card.xpub.to_string(),
"xpub_fingerprint": xpub_fingerprint_hex(card),
"origin_fingerprint": card.origin_fingerprint.as_ref().map(fmt_fingerprint),
"origin_path": card.origin_path.to_string(),
"origin_path_components": path_components,
"policy_id_stubs": stubs,
"chunks": strings.len(),
"chunk_variants": variants,
});
let s = serde_json::to_string(&envelope)
.map_err(|e| CliError::UsageError(format!("json serialization: {e}")))?;
println!("{s}");
Ok(())
}