use std::path::Path;
use anyhow::{Result, anyhow};
use greentic_pack::{SigningPolicy, open_pack};
use serde_json::Value;
use super::info::InfoReport;
use super::info::report::{SignatureInfo, SignatureStatus};
use super::inspect::InspectFormat;
pub(crate) const ERR_NOT_A_PACK: &str = "not a .gtpack file";
pub(crate) const ERR_STRICT_UNSIGNED: &str = "Signature verification failed";
pub fn handle(path: &Path, format: InspectFormat, strict: bool) -> Result<()> {
validate_path(path)?;
let policy = if strict {
SigningPolicy::Strict
} else {
SigningPolicy::DevOk
};
let load = open_pack(path, policy).map_err(|e| {
if strict {
anyhow!("{ERR_STRICT_UNSIGNED}: {}", e.message)
} else {
anyhow!("Failed to read pack: {}", e.message)
}
})?;
if strict && !load.report.signature_ok {
return Err(anyhow!(
"{ERR_STRICT_UNSIGNED}: pack is unsigned or signature is invalid"
));
}
let sig = if load.report.signature_ok {
SignatureInfo {
status: SignatureStatus::Signed,
key_id: None,
}
} else {
SignatureInfo {
status: SignatureStatus::Unsigned,
key_id: None,
}
};
let report = InfoReport::from_pack_meta_and_signature(&load.manifest.meta, sig);
match format {
InspectFormat::Json => {
let value: Value = serde_json::to_value(&report)?;
let sorted = super::inspect::sort_json(value);
println!("{}", serde_json::to_string_pretty(&sorted)?);
}
InspectFormat::Human => {
print!("{}", super::info::human::render(&report));
}
}
Ok(())
}
fn validate_path(path: &Path) -> Result<()> {
if !path.exists() {
return Err(anyhow!(
"{ERR_NOT_A_PACK}: {} (no such file)",
path.display()
));
}
if !path.is_file() {
return Err(anyhow!(
"{ERR_NOT_A_PACK}: {} (not a regular file)",
path.display()
));
}
let ext_ok = path
.extension()
.and_then(|s| s.to_str())
.map(|ext| ext.eq_ignore_ascii_case("gtpack"))
.unwrap_or(false);
if !ext_ok {
return Err(anyhow!(
"{ERR_NOT_A_PACK}: {} (expected .gtpack extension)",
path.display()
));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn validate_path_rejects_missing() {
let err = validate_path(Path::new("/definitely/not/here.gtpack")).unwrap_err();
assert!(err.to_string().starts_with(ERR_NOT_A_PACK));
}
#[test]
fn validate_path_rejects_wrong_extension() {
let manifest = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml");
let err = validate_path(&manifest).unwrap_err();
let msg = err.to_string();
assert!(msg.starts_with(ERR_NOT_A_PACK));
assert!(msg.contains("expected .gtpack extension"));
}
}