1use crate::builder::BundleBuilder;
2use crate::checksum::{parse_checksum, CHECKSUM_LEN};
3use crate::header::{Header, HeaderReader, HeaderWriter};
4use crate::index::{Index, IndexEntry, IndexReader, IndexWriter};
5use crate::reader::Reader;
6use crate::writer::Writer;
7use lz4_flex::decompress_size_prepended;
8use std::io::{Cursor, Read, Seek, SeekFrom, Write};
9
10#[cfg(feature = "async")]
11use crate::{
12 AsyncHeaderReader, AsyncHeaderWriter, AsyncIndexReader, AsyncIndexWriter, AsyncReader,
13 AsyncWriter,
14};
15#[cfg(feature = "async")]
16use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt};
17
18#[derive(Debug, PartialEq, Clone)]
19pub struct BundleManifest {
20 pub(crate) header: Header,
21 pub(crate) index: Index,
22}
23
24impl BundleManifest {
25 pub fn header(&self) -> &Header {
26 &self.header
27 }
28
29 pub fn index(&self) -> &Index {
30 &self.index
31 }
32
33 pub fn get_data<R: Read + Seek>(&self, reader: R, path: &str) -> crate::Result<Option<Vec<u8>>> {
34 if !self.index.contains_path(path) {
35 return Ok(None);
36 }
37 let entry = self.index.get_entry(path).unwrap();
38 let mut reader = BundleDataReader::new(reader, self.header.index_end_offset());
39 let data = reader.read_entry_data(entry)?;
40 Ok(Some(data))
41 }
42
43 pub fn get_data_checksum<R: Read + Seek>(
44 &self,
45 reader: R,
46 path: &str,
47 ) -> crate::Result<Option<u32>> {
48 if !self.index.contains_path(path) {
49 return Ok(None);
50 }
51 let entry = self.index.get_entry(path).unwrap();
52 let mut reader = BundleDataReader::new(reader, self.header.index_end_offset());
53 let checksum = reader.read_entry_checksum(entry)?;
54 Ok(Some(checksum))
55 }
56
57 #[cfg(feature = "async")]
58 pub async fn async_get_data<R: AsyncRead + AsyncSeek + Unpin>(
59 &self,
60 reader: R,
61 path: &str,
62 ) -> crate::Result<Option<Vec<u8>>> {
63 if !self.index.contains_path(path) {
64 return Ok(None);
65 }
66 let entry = self.index.get_entry(path).unwrap();
67 let mut reader = AsyncBundleDataReader::new(reader, self.header.index_end_offset());
68 let data = reader.read_entry_data(entry).await?;
69 Ok(Some(data))
70 }
71
72 #[cfg(feature = "async")]
73 pub async fn async_get_data_checksum<R: AsyncRead + AsyncSeek + Unpin>(
74 &self,
75 reader: R,
76 path: &str,
77 ) -> crate::Result<Option<u32>> {
78 if !self.index.contains_path(path) {
79 return Ok(None);
80 }
81 let entry = self.index.get_entry(path).unwrap();
82 let mut reader = AsyncBundleDataReader::new(reader, self.header.index_end_offset());
83 let data = reader.read_entry_checksum(entry).await?;
84 Ok(Some(data))
85 }
86}
87
88#[derive(Debug, PartialEq, Clone)]
89pub struct Bundle {
90 pub(crate) manifest: BundleManifest,
91 pub(crate) data: Vec<u8>,
92}
93
94impl Bundle {
95 pub fn builder() -> BundleBuilder {
96 BundleBuilder::new()
97 }
98
99 pub fn builder_with_capacity(capacity: usize) -> BundleBuilder {
100 BundleBuilder::new_with_capacity(capacity)
101 }
102
103 pub fn manifest(&self) -> &BundleManifest {
104 &self.manifest
105 }
106
107 pub fn get_data(&self, path: &str) -> crate::Result<Option<Vec<u8>>> {
108 if !self.manifest.index.contains_path(path) {
109 return Ok(None);
110 }
111 let entry = self.manifest.index.get_entry(path).unwrap();
112 let mut reader = BundleDataReader::new(Cursor::new(&self.data), 0);
113 let data = reader.read_entry_data(entry)?;
114 Ok(Some(data))
115 }
116
117 pub fn get_data_checksum(&self, path: &str) -> crate::Result<Option<u32>> {
118 if !self.manifest.index.contains_path(path) {
119 return Ok(None);
120 }
121 let entry = self.manifest.index.get_entry(path).unwrap();
122 let mut reader = BundleDataReader::new(Cursor::new(&self.data), 0);
123 let checksum = reader.read_entry_checksum(entry)?;
124 Ok(Some(checksum))
125 }
126}
127
128fn read_entry(entry: &IndexEntry) -> (u64, Vec<u8>) {
129 (entry.offset(), vec![0u8; entry.len() as usize])
130}
131
132fn parse_entry(buf: &[u8]) -> crate::Result<Vec<u8>> {
133 let decompressed = decompress_size_prepended(buf)?;
134 Ok(decompressed)
135}
136
137fn read_entry_checksum(entry: &IndexEntry) -> (u64, [u8; CHECKSUM_LEN]) {
138 (entry.offset() + entry.len(), [0u8; CHECKSUM_LEN])
139}
140
141pub(crate) struct BundleDataReader<R: Read + Seek> {
142 r: R,
143 base_offset: u64,
144}
145
146impl<R: Read + Seek> BundleDataReader<R> {
147 pub fn new(r: R, base_offset: u64) -> Self {
148 Self { r, base_offset }
149 }
150
151 pub fn read_entry_data(&mut self, entry: &IndexEntry) -> crate::Result<Vec<u8>> {
152 let (offset, mut buf) = read_entry(entry);
153 self.r.seek(SeekFrom::Start(self.base_offset + offset))?;
154 self.r.read_exact(&mut buf)?;
155 parse_entry(&buf)
156 }
157
158 pub fn read_entry_checksum(&mut self, entry: &IndexEntry) -> crate::Result<u32> {
159 let (offset, mut buf) = read_entry_checksum(entry);
160 self.r.seek(SeekFrom::Start(self.base_offset + offset))?;
161 self.r.read_exact(&mut buf)?;
162 Ok(parse_checksum(&buf))
163 }
164}
165
166#[cfg(feature = "async")]
167pub(crate) struct AsyncBundleDataReader<R: AsyncRead + AsyncSeek + Unpin> {
168 r: R,
169 base_offset: u64,
170}
171
172#[cfg(feature = "async")]
173impl<R: AsyncRead + AsyncSeek + Unpin> AsyncBundleDataReader<R> {
174 pub fn new(r: R, base_offset: u64) -> Self {
175 Self { r, base_offset }
176 }
177
178 pub async fn read_entry_data(&mut self, entry: &IndexEntry) -> crate::Result<Vec<u8>> {
179 let (offset, mut buf) = read_entry(entry);
180 self
181 .r
182 .seek(SeekFrom::Start(self.base_offset + offset))
183 .await?;
184 self.r.read_exact(&mut buf).await?;
185 parse_entry(&buf)
186 }
187
188 pub async fn read_entry_checksum(&mut self, entry: &IndexEntry) -> crate::Result<u32> {
189 let (offset, mut buf) = read_entry_checksum(entry);
190 self
191 .r
192 .seek(SeekFrom::Start(self.base_offset + offset))
193 .await?;
194 self.r.read_exact(&mut buf).await?;
195 Ok(parse_checksum(&buf))
196 }
197}
198
199pub struct BundleReader<R: Read + Seek> {
200 r: R,
201}
202
203impl<R: Read + Seek> BundleReader<R> {
204 pub fn new(r: R) -> Self {
205 Self { r }
206 }
207
208 pub fn read_header(&mut self) -> crate::Result<Header> {
209 let mut reader = HeaderReader::new(&mut self.r);
210 let header = reader.read()?;
211 Ok(header)
212 }
213
214 pub fn read_index(&mut self, header: Header) -> crate::Result<Index> {
215 let mut reader = IndexReader::new(&mut self.r, header);
216 let index = reader.read()?;
217 Ok(index)
218 }
219
220 pub fn read_data(&mut self, header: Header) -> crate::Result<Vec<u8>> {
221 self.r.seek(SeekFrom::Start(header.index_end_offset()))?;
222 let mut data = vec![];
223 self.r.read_to_end(&mut data)?;
224 Ok(data)
225 }
226}
227
228impl<R: Read + Seek> Reader<BundleManifest> for BundleReader<R> {
229 fn read(&mut self) -> crate::Result<BundleManifest> {
230 let header = self.read_header()?;
231 let index = self.read_index(header)?;
232 Ok(BundleManifest { header, index })
233 }
234}
235
236impl<R: Read + Seek> Reader<Bundle> for BundleReader<R> {
237 fn read(&mut self) -> crate::Result<Bundle> {
238 let header = self.read_header()?;
239 let index = self.read_index(header)?;
240 let data = self.read_data(header)?;
241 Ok(Bundle {
242 manifest: BundleManifest { header, index },
243 data,
244 })
245 }
246}
247
248#[cfg(feature = "async")]
249pub struct AsyncBundleReader<R: AsyncRead + AsyncSeek + Unpin> {
250 r: R,
251}
252
253#[cfg(feature = "async")]
254impl<R: AsyncRead + AsyncSeek + Unpin> AsyncBundleReader<R> {
255 pub fn new(r: R) -> Self {
256 Self { r }
257 }
258
259 pub async fn read_header(&mut self) -> crate::Result<Header> {
260 let mut reader = AsyncHeaderReader::new(&mut self.r);
261 let header = reader.read().await?;
262 Ok(header)
263 }
264
265 pub async fn read_index(&mut self, header: Header) -> crate::Result<Index> {
266 let mut reader = AsyncIndexReader::new(&mut self.r, header);
267 let index = reader.read().await?;
268 Ok(index)
269 }
270
271 pub async fn read_data(&mut self, header: Header) -> crate::Result<Vec<u8>> {
272 self
273 .r
274 .seek(SeekFrom::Start(header.index_end_offset()))
275 .await?;
276 let mut data = vec![];
277 self.r.read_to_end(&mut data).await?;
278 Ok(data)
279 }
280}
281
282#[cfg(feature = "async")]
283impl<R: AsyncRead + AsyncSeek + Unpin> AsyncReader<BundleManifest> for AsyncBundleReader<R> {
284 async fn read(&mut self) -> crate::Result<BundleManifest> {
285 let header = self.read_header().await?;
286 let index = self.read_index(header).await?;
287 Ok(BundleManifest { header, index })
288 }
289}
290
291#[cfg(feature = "async")]
292impl<R: AsyncRead + AsyncSeek + Unpin> AsyncReader<Bundle> for AsyncBundleReader<R> {
293 async fn read(&mut self) -> crate::Result<Bundle> {
294 let header = self.read_header().await?;
295 let index = self.read_index(header).await?;
296 let data = self.read_data(header).await?;
297 Ok(Bundle {
298 manifest: BundleManifest { header, index },
299 data,
300 })
301 }
302}
303
304pub struct BundleWriter<W: Write> {
305 w: W,
306}
307
308impl<W: Write> BundleWriter<W> {
309 pub fn new(w: W) -> Self {
310 Self { w }
311 }
312}
313
314impl<W: Write> Writer<Bundle> for BundleWriter<W> {
315 fn write(&mut self, data: &Bundle) -> crate::Result<usize> {
316 let header_len = HeaderWriter::new(&mut self.w).write(&data.manifest.header)?;
317 let index_len = IndexWriter::new(&mut self.w).write(&data.manifest.index)?;
318 let data_len = data.data.len();
319 self.w.write_all(&data.data)?;
320 self.w.flush()?;
321 Ok(header_len + index_len + data_len)
322 }
323}
324
325#[cfg(feature = "async")]
326pub struct AsyncBundleWriter<W: AsyncWrite + Unpin> {
327 w: W,
328}
329
330#[cfg(feature = "async")]
331impl<W: AsyncWrite + Unpin> AsyncBundleWriter<W> {
332 pub fn new(w: W) -> Self {
333 Self { w }
334 }
335}
336
337#[cfg(feature = "async")]
338impl<W: AsyncWrite + Unpin> AsyncWriter<Bundle> for AsyncBundleWriter<W> {
339 async fn write(&mut self, data: &Bundle) -> crate::Result<usize> {
340 let header_len = AsyncHeaderWriter::new(&mut self.w)
341 .write(&data.manifest.header)
342 .await?;
343 let index_len = AsyncIndexWriter::new(&mut self.w)
344 .write(&data.manifest.index)
345 .await?;
346 let data_len = data.data.len();
347 self.w.write_all(&data.data).await?;
348 self.w.flush().await?;
349 Ok(header_len + index_len + data_len)
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356 use crate::version::Version;
357 use http::{header, HeaderMap};
358 use std::io::Cursor;
359
360 const INDEX_HTML: &str = r#"<!DOCTYPE html>
361<html>
362<head>
363 <title>test</title>
364</head>
365<body>
366 <h1>Hello World</h1>
367</body>
368</html>
369"#;
370 const INDEX_JS: &str = r#"console.log('Hello World');"#;
371
372 #[test]
373 fn manifest() {
374 let mut builder = Bundle::builder();
375 let mut headers = HeaderMap::new();
376 headers.insert(header::CONTENT_TYPE, "text/html".parse().unwrap());
377 builder.insert_entry("/index.html", (INDEX_HTML.as_bytes(), headers));
378 let bundle = builder.build().unwrap();
379 let mut data = vec![];
380 let mut writer = BundleWriter::new(Cursor::new(&mut data));
381 let size = writer.write(&bundle).unwrap();
382 assert_eq!(size, 162);
383 let mut reader = BundleReader::new(Cursor::new(&data));
384 let manifest: BundleManifest = reader.read().unwrap();
385 assert_eq!(manifest.header.version(), Version::V1);
386 assert_eq!(manifest.header.index_size(), 39);
387
388 let html = manifest.index.get_entry("/index.html").unwrap();
389 assert_eq!(
390 html.headers().get(header::CONTENT_TYPE).unwrap(),
391 "text/html"
392 );
393 assert_eq!(html.offset(), 0);
394 assert_eq!(html.len(), 98);
395 }
396
397 #[test]
398 fn get_data() {
399 let mut builder = Bundle::builder();
400 let mut headers = HeaderMap::new();
401 headers.insert(header::CONTENT_TYPE, "text/html".parse().unwrap());
402 builder.insert_entry("/index.html", (INDEX_HTML.as_bytes(), headers));
403 builder.insert_entry("/index.js", INDEX_JS.as_bytes());
404 let bundle = builder.build().unwrap();
405 let mut data = vec![];
406 let mut writer = BundleWriter::new(Cursor::new(&mut data));
407 let size = writer.write(&bundle).unwrap();
408 assert_eq!(size, 212);
409 let mut reader = BundleReader::new(Cursor::new(&data));
410 let bundle: Bundle = reader.read().unwrap();
411
412 let html = bundle.get_data("/index.html").unwrap().unwrap();
413 assert_eq!(html, INDEX_HTML.as_bytes());
414
415 let js = bundle.get_data("/index.js").unwrap().unwrap();
416 assert_eq!(js, INDEX_JS.as_bytes());
417
418 assert!(bundle.get_data("/not_found.html").unwrap().is_none());
420 }
421
422 #[cfg(feature = "async")]
423 #[tokio::test]
424 async fn async_get_data() {
425 let mut builder = Bundle::builder();
426 let mut headers = HeaderMap::new();
427 headers.insert(header::CONTENT_TYPE, "text/html".parse().unwrap());
428 builder.insert_entry("/index.html", (INDEX_HTML.as_bytes(), headers));
429 builder.insert_entry("/index.js", INDEX_JS.as_bytes());
430 let bundle = builder.build().unwrap();
431 let mut data = vec![];
432 let mut writer = BundleWriter::new(Cursor::new(&mut data));
433 writer.write(&bundle).unwrap();
434 let mut reader = BundleReader::new(Cursor::new(&data));
435 let manifest: BundleManifest = reader.read().unwrap();
436 let html = manifest
437 .async_get_data(Cursor::new(&data), "/index.html")
438 .await
439 .unwrap();
440 assert_eq!(html.unwrap(), INDEX_HTML.as_bytes());
441 }
442}