use anyhow::Result;
use std::path::Path;
pub fn build_qr_content(
manifest_path: &Path,
pubkey_override: Option<&str>,
_ec_level: Option<qrcode::EcLevel>,
) -> Result<String> {
use sha2::{Digest, Sha256};
let bytes = std::fs::read(manifest_path)?;
let mut hasher = Sha256::new();
hasher.update(&bytes);
let manifest_sha256 = hex::encode(hasher.finalize());
let text = String::from_utf8_lossy(&bytes);
let mut case_id: Option<String> = None;
let mut examiner: Option<String> = None;
for line in text.lines() {
if let Some(rest) = line.strip_prefix("## case:") {
case_id = Some(rest.trim().to_string());
} else if let Some(rest) = line.strip_prefix("## examiner:") {
examiner = Some(rest.trim().to_string());
}
}
let pubkey: Option<String> = if let Some(pk) = pubkey_override {
let pk = pk.trim().to_string();
if !pk.is_empty() && pk.len() <= 64 {
Some(pk)
} else {
None
}
} else {
let pub_path = {
let mut p = manifest_path.to_path_buf();
let name = p
.file_name()
.map(|n| format!("{}.pub", n.to_string_lossy()))
.unwrap_or_default();
p.set_file_name(name);
p
};
if pub_path.exists() {
let raw = std::fs::read_to_string(&pub_path)?;
let trimmed = raw.trim().to_string();
if trimmed.len() <= 64 {
Some(trimmed)
} else {
None
}
} else {
None
}
};
let mut parts: Vec<String> = vec![format!("sha256={manifest_sha256}")];
if let Some(pk) = pubkey {
parts.push(format!("pubkey={pk}"));
}
if let Some(c) = case_id {
parts.push(format!("case={c}"));
}
if let Some(e) = examiner {
let encoded = e.replace(' ', "+");
parts.push(format!("examiner={encoded}"));
}
Ok(format!("BLAZEHASH:{}", parts.join("&")))
}
pub fn generate_qr_text(manifest_path: &Path, pubkey_override: Option<&str>) -> Result<String> {
let content = build_qr_content(manifest_path, pubkey_override, None)?;
let code = qrcode::QrCode::with_error_correction_level(content.as_bytes(), qrcode::EcLevel::M)
.map_err(|e| anyhow::anyhow!("QR encoding error: {e:?}"))?;
Ok(code
.render::<qrcode::render::unicode::Dense1x2>()
.quiet_zone(true)
.build())
}
pub fn generate_qr_png(
manifest_path: &Path,
out_path: &Path,
pubkey_override: Option<&str>,
) -> Result<()> {
let content = build_qr_content(manifest_path, pubkey_override, None)?;
let code = qrcode::QrCode::with_error_correction_level(
content.as_bytes(),
qrcode::EcLevel::M,
)
.map_err(|e| anyhow::anyhow!("QR encoding error: {e:?}"))?;
let img = code
.render::<image::Luma<u8>>()
.quiet_zone(true)
.module_dimensions(8, 8)
.build();
img.save(out_path)
.map_err(|e| anyhow::anyhow!("failed to write PNG: {e}"))?;
Ok(())
}