1use std::io::Cursor;
2
3use crate::error::PackError;
4use crate::manifest::PackageManifest;
5use crate::util::read_zip_entry;
6
7pub fn read_manifest(actr_bytes: &[u8]) -> Result<PackageManifest, PackError> {
9 let manifest_str = read_manifest_raw(actr_bytes)?;
10 PackageManifest::from_toml(&manifest_str)
11}
12
13pub fn read_manifest_raw(actr_bytes: &[u8]) -> Result<String, PackError> {
18 let cursor = Cursor::new(actr_bytes);
19 let mut archive = zip::ZipArchive::new(cursor)?;
20
21 let manifest_bytes =
22 read_zip_entry(&mut archive, "manifest.toml").map_err(|_| PackError::ManifestNotFound)?;
23
24 String::from_utf8(manifest_bytes)
25 .map_err(|e| PackError::ManifestParseError(format!("manifest is not valid UTF-8: {e}")))
26}
27
28pub fn load_binary(actr_bytes: &[u8]) -> Result<Vec<u8>, PackError> {
32 let cursor = Cursor::new(actr_bytes);
33 let mut archive = zip::ZipArchive::new(cursor)?;
34
35 let manifest_bytes =
37 read_zip_entry(&mut archive, "manifest.toml").map_err(|_| PackError::ManifestNotFound)?;
38 let manifest_str = std::str::from_utf8(&manifest_bytes)
39 .map_err(|e| PackError::ManifestParseError(format!("manifest is not valid UTF-8: {e}")))?;
40 let manifest = PackageManifest::from_toml(manifest_str)?;
41
42 read_zip_entry(&mut archive, &manifest.binary.path)
43 .map_err(|_| PackError::BinaryNotFound(manifest.binary.path.clone()))
44}
45
46pub fn read_signature(actr_bytes: &[u8]) -> Result<Vec<u8>, PackError> {
48 let cursor = Cursor::new(actr_bytes);
49 let mut archive = zip::ZipArchive::new(cursor)?;
50 let sig =
51 read_zip_entry(&mut archive, "manifest.sig").map_err(|_| PackError::SignatureNotFound)?;
52 if sig.len() != 64 {
53 return Err(PackError::SignatureVerificationFailed(format!(
54 "manifest.sig must be exactly 64 bytes, got {}",
55 sig.len()
56 )));
57 }
58 Ok(sig)
59}
60
61pub fn read_lock_file(actr_bytes: &[u8]) -> Result<Option<Vec<u8>>, PackError> {
65 let cursor = Cursor::new(actr_bytes);
66 let mut archive = zip::ZipArchive::new(cursor)?;
67 match read_zip_entry(&mut archive, "manifest.lock.toml") {
68 Ok(bytes) => Ok(Some(bytes)),
69 Err(_) => Ok(None),
70 }
71}
72
73pub fn read_glue_js(actr_bytes: &[u8]) -> Result<Option<String>, PackError> {
83 use std::io::Read;
84
85 let cursor = Cursor::new(actr_bytes);
86 let mut archive = zip::ZipArchive::new(cursor)?;
87
88 let target = archive
89 .file_names()
90 .find(|name| {
91 name.starts_with("resources/")
92 && name.ends_with(".js")
93 && !name.ends_with("actor.sw.js")
94 })
95 .map(|s| s.to_string());
96
97 let Some(name) = target else {
98 return Ok(None);
99 };
100
101 let mut file = archive
102 .by_name(&name)
103 .map_err(|e| PackError::InvalidPackage(format!("open {}: {}", name, e)))?;
104 let mut content = String::new();
105 file.read_to_string(&mut content)
106 .map_err(|e| PackError::InvalidPackage(format!("read {}: {}", name, e)))?;
107 Ok(Some(content))
108}
109
110pub fn read_proto_files(actr_bytes: &[u8]) -> Result<Vec<(String, Vec<u8>)>, PackError> {
115 let cursor = Cursor::new(actr_bytes);
116 let archive = zip::ZipArchive::new(cursor)?;
117
118 let proto_names: Vec<String> = archive
119 .file_names()
120 .filter(|name| name.starts_with("proto/") && name.len() > "proto/".len())
121 .map(|s| s.to_string())
122 .collect();
123
124 let mut result = Vec::new();
125 let cursor2 = Cursor::new(actr_bytes);
127 let mut archive2 = zip::ZipArchive::new(cursor2)?;
128
129 for full_path in proto_names {
130 let filename = full_path
131 .strip_prefix("proto/")
132 .unwrap_or(&full_path)
133 .to_string();
134 let content = read_zip_entry(&mut archive2, &full_path)?;
135 result.push((filename, content));
136 }
137
138 Ok(result)
139}