1use std::collections::HashMap;
2use std::io::{self, Cursor, Read, Write};
3
4use crate::entry::{Entry, EntryPoint};
5
6pub const MANIFEST_HEADER_SIZE: usize = 2 + 4 + 4 + 2 + 2 + 2 + 2 + 32; #[derive(Debug, Clone)]
9pub struct ManifestHeader {
10 pub version: u16,
12 pub entry_count: u32,
14 pub string_table_size: u32,
16 pub entrypoint_count: u16,
18 pub default_entrypoint: u16,
20 pub lib_dir_count: u16,
22 pub name_offset: u16,
24 pub package_id: [u8; 32],
26}
27
28impl ManifestHeader {
29 pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> {
30 w.write_all(&self.version.to_le_bytes())?;
31 w.write_all(&self.entry_count.to_le_bytes())?;
32 w.write_all(&self.string_table_size.to_le_bytes())?;
33 w.write_all(&self.entrypoint_count.to_le_bytes())?;
34 w.write_all(&self.default_entrypoint.to_le_bytes())?;
35 w.write_all(&self.lib_dir_count.to_le_bytes())?;
36 w.write_all(&self.name_offset.to_le_bytes())?;
37 w.write_all(&self.package_id)?;
38 Ok(())
39 }
40
41 pub fn read_from<R: Read>(r: &mut R) -> io::Result<Self> {
42 let mut buf = [0u8; MANIFEST_HEADER_SIZE];
43 r.read_exact(&mut buf)?;
44
45 let mut package_id = [0u8; 32];
46 package_id.copy_from_slice(&buf[18..50]);
47
48 Ok(ManifestHeader {
49 version: u16::from_le_bytes(buf[0..2].try_into().unwrap()),
50 entry_count: u32::from_le_bytes(buf[2..6].try_into().unwrap()),
51 string_table_size: u32::from_le_bytes(buf[6..10].try_into().unwrap()),
52 entrypoint_count: u16::from_le_bytes(buf[10..12].try_into().unwrap()),
53 default_entrypoint: u16::from_le_bytes(buf[12..14].try_into().unwrap()),
54 lib_dir_count: u16::from_le_bytes(buf[14..16].try_into().unwrap()),
55 name_offset: u16::from_le_bytes(buf[16..18].try_into().unwrap()),
56 package_id,
57 })
58 }
59}
60
61#[derive(Debug, Clone)]
62pub struct Manifest {
63 pub header: ManifestHeader,
65 pub entrypoints: Vec<EntryPoint>,
67 pub entries: Vec<Entry>,
69 pub lib_dir_offsets: Vec<u32>,
71 pub string_table: Vec<u8>,
73}
74
75impl Manifest {
76 pub fn serialize(&self) -> io::Result<Vec<u8>> {
77 let mut buf = Vec::new();
78 self.header.write_to(&mut buf)?;
79 for ep in &self.entrypoints {
80 ep.write_to(&mut buf)?;
81 }
82 for entry in &self.entries {
83 entry.write_to(&mut buf)?;
84 }
85 for &offset in &self.lib_dir_offsets {
86 buf.write_all(&offset.to_le_bytes())?;
87 }
88 buf.write_all(&self.string_table)?;
89 Ok(buf)
90 }
91
92 pub fn deserialize(data: &[u8]) -> io::Result<Self> {
93 let mut cursor = Cursor::new(data);
94 let header = ManifestHeader::read_from(&mut cursor)?;
95
96 if header.version != 1 {
97 return Err(io::Error::new(
98 io::ErrorKind::InvalidData,
99 format!("unsupported manifest version: {}", header.version),
100 ));
101 }
102
103 let mut entrypoints = Vec::with_capacity(header.entrypoint_count as usize);
104 for _ in 0..header.entrypoint_count {
105 entrypoints.push(EntryPoint::read_from(&mut cursor)?);
106 }
107
108 let mut entries = Vec::with_capacity(header.entry_count as usize);
109 for _ in 0..header.entry_count {
110 entries.push(Entry::read_from(&mut cursor)?);
111 }
112
113 let mut lib_dir_offsets = Vec::with_capacity(header.lib_dir_count as usize);
114 for _ in 0..header.lib_dir_count {
115 let mut offset_buf = [0u8; 4];
116 cursor.read_exact(&mut offset_buf)?;
117 lib_dir_offsets.push(u32::from_le_bytes(offset_buf));
118 }
119
120 let mut string_table = vec![0u8; header.string_table_size as usize];
121 cursor.read_exact(&mut string_table)?;
122
123 Ok(Manifest {
124 header,
125 entrypoints,
126 entries,
127 lib_dir_offsets,
128 string_table,
129 })
130 }
131
132 pub fn name(&self) -> &str {
134 if self.header.name_offset > 0 {
135 self.get_string(self.header.name_offset as u32)
136 } else {
137 ""
138 }
139 }
140
141 pub fn lib_dirs(&self) -> Vec<&str> {
143 self.lib_dir_offsets
144 .iter()
145 .map(|&offset| self.get_string(offset))
146 .collect()
147 }
148
149 pub fn get_string(&self, offset: u32) -> &str {
150 let start = offset as usize;
151 let end = self.string_table[start..]
152 .iter()
153 .position(|&b| b == 0)
154 .map(|p| start + p)
155 .unwrap_or(self.string_table.len());
156 std::str::from_utf8(&self.string_table[start..end]).unwrap_or("")
157 }
158
159 pub fn has_toplevel_dir(&self, name: &str) -> bool {
161 use crate::entry::EntryKind;
162 self.entries.iter().any(|e| {
163 e.kind == EntryKind::Dir && e.parent == u32::MAX && self.get_string(e.name) == name
164 })
165 }
166
167 pub fn find_lib_dir(&self) -> String {
170 use crate::entry::EntryKind;
171 for (i, e) in self.entries.iter().enumerate() {
172 if e.kind == EntryKind::Dir && self.get_string(e.name) == "lib" {
173 return self.entry_path(i);
174 }
175 }
176 String::new()
177 }
178
179 pub fn entry_path(&self, index: usize) -> String {
181 let mut parts = Vec::new();
182 let mut idx = index;
183 loop {
184 let entry = &self.entries[idx];
185 let name = self.get_string(entry.name);
186 if name.is_empty() {
187 break;
188 }
189 parts.push(name);
190 if entry.parent == u32::MAX {
191 break;
192 }
193 idx = entry.parent as usize;
194 }
195 parts.reverse();
196 parts.join("/")
197 }
198}
199
200#[derive(Debug, Default)]
202pub struct StringTableBuilder {
203 data: Vec<u8>,
204 index: HashMap<String, u32>,
205}
206
207impl StringTableBuilder {
208 pub fn new() -> Self {
209 Self {
210 data: Vec::new(),
211 index: HashMap::new(),
212 }
213 }
214
215 pub fn add(&mut self, s: &str) -> u32 {
217 if let Some(&offset) = self.index.get(s) {
218 return offset;
219 }
220 let offset = self.data.len() as u32;
221 self.data.extend_from_slice(s.as_bytes());
222 self.data.push(0);
223 self.index.insert(s.to_owned(), offset);
224 offset
225 }
226
227 pub fn finish(self) -> Vec<u8> {
228 self.data
229 }
230
231 pub fn len(&self) -> u32 {
232 self.data.len() as u32
233 }
234}