use std::io::Cursor;
use crate::error::PackError;
use crate::manifest::PackageManifest;
use crate::util::read_zip_entry;
pub fn read_manifest(actr_bytes: &[u8]) -> Result<PackageManifest, PackError> {
let manifest_str = read_manifest_raw(actr_bytes)?;
PackageManifest::from_toml(&manifest_str)
}
pub fn read_manifest_raw(actr_bytes: &[u8]) -> Result<String, PackError> {
let cursor = Cursor::new(actr_bytes);
let mut archive = zip::ZipArchive::new(cursor)?;
let manifest_bytes =
read_zip_entry(&mut archive, "manifest.toml").map_err(|_| PackError::ManifestNotFound)?;
String::from_utf8(manifest_bytes)
.map_err(|e| PackError::ManifestParseError(format!("manifest is not valid UTF-8: {e}")))
}
pub fn load_binary(actr_bytes: &[u8]) -> Result<Vec<u8>, PackError> {
let cursor = Cursor::new(actr_bytes);
let mut archive = zip::ZipArchive::new(cursor)?;
let manifest_bytes =
read_zip_entry(&mut archive, "manifest.toml").map_err(|_| PackError::ManifestNotFound)?;
let manifest_str = std::str::from_utf8(&manifest_bytes)
.map_err(|e| PackError::ManifestParseError(format!("manifest is not valid UTF-8: {e}")))?;
let manifest = PackageManifest::from_toml(manifest_str)?;
read_zip_entry(&mut archive, &manifest.binary.path)
.map_err(|_| PackError::BinaryNotFound(manifest.binary.path.clone()))
}
pub fn read_signature(actr_bytes: &[u8]) -> Result<Vec<u8>, PackError> {
let cursor = Cursor::new(actr_bytes);
let mut archive = zip::ZipArchive::new(cursor)?;
let sig =
read_zip_entry(&mut archive, "manifest.sig").map_err(|_| PackError::SignatureNotFound)?;
if sig.len() != 64 {
return Err(PackError::SignatureVerificationFailed(format!(
"manifest.sig must be exactly 64 bytes, got {}",
sig.len()
)));
}
Ok(sig)
}
pub fn read_lock_file(actr_bytes: &[u8]) -> Result<Option<Vec<u8>>, PackError> {
let cursor = Cursor::new(actr_bytes);
let mut archive = zip::ZipArchive::new(cursor)?;
match read_zip_entry(&mut archive, "manifest.lock.toml") {
Ok(bytes) => Ok(Some(bytes)),
Err(_) => Ok(None),
}
}
pub fn read_glue_js(actr_bytes: &[u8]) -> Result<Option<String>, PackError> {
use std::io::Read;
let cursor = Cursor::new(actr_bytes);
let mut archive = zip::ZipArchive::new(cursor)?;
let target = archive
.file_names()
.find(|name| {
name.starts_with("resources/")
&& name.ends_with(".js")
&& !name.ends_with("actor.sw.js")
})
.map(|s| s.to_string());
let Some(name) = target else {
return Ok(None);
};
let mut file = archive
.by_name(&name)
.map_err(|e| PackError::InvalidPackage(format!("open {}: {}", name, e)))?;
let mut content = String::new();
file.read_to_string(&mut content)
.map_err(|e| PackError::InvalidPackage(format!("read {}: {}", name, e)))?;
Ok(Some(content))
}
pub fn read_proto_files(actr_bytes: &[u8]) -> Result<Vec<(String, Vec<u8>)>, PackError> {
let cursor = Cursor::new(actr_bytes);
let archive = zip::ZipArchive::new(cursor)?;
let proto_names: Vec<String> = archive
.file_names()
.filter(|name| name.starts_with("proto/") && name.len() > "proto/".len())
.map(|s| s.to_string())
.collect();
let mut result = Vec::new();
let cursor2 = Cursor::new(actr_bytes);
let mut archive2 = zip::ZipArchive::new(cursor2)?;
for full_path in proto_names {
let filename = full_path
.strip_prefix("proto/")
.unwrap_or(&full_path)
.to_string();
let content = read_zip_entry(&mut archive2, &full_path)?;
result.push((filename, content));
}
Ok(result)
}