webview_bundle/
bundle.rs

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    // Not found
419    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}