webview_bundle/
builder.rs1use crate::checksum::{make_checksum, CHECKSUM_LEN};
2use crate::header::HeaderWriterOptions;
3use crate::index::{Index, IndexEntry, IndexWriterOptions};
4use crate::version::Version;
5use crate::{Bundle, BundleManifest, Header, IndexWriter, Writer};
6use http::HeaderMap;
7use lz4_flex::compress_prepend_size;
8use std::collections::HashMap;
9
10#[derive(Debug, PartialEq, Clone)]
11pub struct BundleEntry {
12 compressed: Vec<u8>,
13 pub headers: Option<HeaderMap>,
14}
15
16impl BundleEntry {
17 pub fn new(data: &[u8], headers: Option<HeaderMap>) -> Self {
18 let compressed = compress_prepend_size(data);
19 Self {
20 compressed,
21 headers,
22 }
23 }
24
25 pub fn data(&self) -> &[u8] {
26 &self.compressed
27 }
28
29 pub fn is_empty(&self) -> bool {
30 self.compressed.is_empty()
31 }
32
33 pub fn len(&self) -> usize {
34 self.compressed.len()
35 }
36}
37
38impl From<&[u8]> for BundleEntry {
39 fn from(data: &[u8]) -> Self {
40 Self::new(data, None)
41 }
42}
43
44impl From<Vec<u8>> for BundleEntry {
45 fn from(data: Vec<u8>) -> Self {
46 Self::new(&data, None)
47 }
48}
49
50impl From<(&[u8], Option<HeaderMap>)> for BundleEntry {
51 fn from((data, headers): (&[u8], Option<HeaderMap>)) -> Self {
52 Self::new(data, headers)
53 }
54}
55
56impl From<(Vec<u8>, Option<HeaderMap>)> for BundleEntry {
57 fn from((data, headers): (Vec<u8>, Option<HeaderMap>)) -> Self {
58 Self::new(&data, headers)
59 }
60}
61
62impl From<(&[u8], HeaderMap)> for BundleEntry {
63 fn from((data, headers): (&[u8], HeaderMap)) -> Self {
64 Self::new(data, Some(headers))
65 }
66}
67
68impl From<(Vec<u8>, HeaderMap)> for BundleEntry {
69 fn from((data, headers): (Vec<u8>, HeaderMap)) -> Self {
70 Self::new(&data, Some(headers))
71 }
72}
73
74#[derive(Debug, Default, Clone, Copy, PartialEq)]
75pub struct BundleBuilderOptions {
76 pub(crate) header: HeaderWriterOptions,
77 pub(crate) index: IndexWriterOptions,
78 pub(crate) data_checksum_seed: u32,
79}
80
81impl BundleBuilderOptions {
82 pub fn new() -> Self {
83 Self::default()
84 }
85
86 pub fn header(&mut self, options: HeaderWriterOptions) -> &mut Self {
87 self.header = options;
88 self
89 }
90
91 pub fn index(&mut self, options: IndexWriterOptions) -> &mut Self {
92 self.index = options;
93 self
94 }
95
96 pub fn data_checksum_seed(&mut self, seed: u32) -> &mut Self {
97 self.data_checksum_seed = seed;
98 self
99 }
100}
101
102#[derive(Debug, Default)]
103pub struct BundleBuilder {
104 entries: HashMap<String, BundleEntry>,
105 version: Version,
106 options: BundleBuilderOptions,
107}
108
109impl BundleBuilder {
110 pub fn new() -> Self {
111 Self::default()
112 }
113
114 pub fn new_with_capacity(capacity: usize) -> Self {
115 Self {
116 entries: HashMap::with_capacity(capacity),
117 ..Self::default()
118 }
119 }
120
121 pub fn new_with_options(options: BundleBuilderOptions) -> Self {
122 Self {
123 options,
124 ..Self::default()
125 }
126 }
127
128 pub fn version(&self) -> Version {
129 self.version
130 }
131
132 pub fn set_version(&mut self, version: Version) -> &mut Self {
133 self.version = version;
134 self
135 }
136
137 pub fn options(&self) -> &BundleBuilderOptions {
138 &self.options
139 }
140
141 pub fn set_options(&mut self, options: BundleBuilderOptions) -> &mut Self {
142 self.options = options;
143 self
144 }
145
146 pub fn entries(&self) -> &HashMap<String, BundleEntry> {
147 &self.entries
148 }
149
150 pub fn insert_entry<S: Into<String>, E: Into<BundleEntry>>(
151 &mut self,
152 path: S,
153 entry: E,
154 ) -> Option<BundleEntry> {
155 let p: String = path.into();
156 let e: BundleEntry = entry.into();
157 self.entries.insert(p, e)
158 }
159
160 pub fn get_entry(&self, path: &str) -> Option<&BundleEntry> {
161 self.entries.get(path)
162 }
163
164 pub fn get_entry_mut(&mut self, path: &str) -> Option<&mut BundleEntry> {
165 self.entries.get_mut(path)
166 }
167
168 pub fn remove_entry(&mut self, path: &str) -> Option<BundleEntry> {
169 self.entries.remove(path)
170 }
171
172 pub fn contains_path(&self, path: &str) -> bool {
173 self.entries.contains_key(path)
174 }
175
176 pub fn build(&self) -> crate::Result<Bundle> {
177 let index = self.build_index();
178 let header = self.build_header(&index)?;
179 let manifest = BundleManifest { header, index };
180 let data = self.build_data();
181 Ok(Bundle { manifest, data })
182 }
183
184 pub(crate) fn build_header(&self, index: &Index) -> crate::Result<Header> {
185 let index_bytes_size =
186 IndexWriter::new_with_options(&mut vec![], self.options().index).write(index)?;
187 let index_size = (index_bytes_size - CHECKSUM_LEN) as u32;
188 let header = Header::new(self.version(), index_size);
189 Ok(header)
190 }
191
192 pub(crate) fn build_index(&self) -> Index {
193 let mut index = Index::new_with_capacity(self.entries().len());
194 let mut offset = 0;
195 for (path, entry) in self.entries() {
196 let len = entry.len() as u64;
197 let mut index_entry = IndexEntry::new(offset, len);
198 if let Some(headers) = entry.headers.as_ref() {
199 index_entry.headers.clone_from(headers);
200 }
201 index.insert_entry(path, index_entry);
202 offset += len;
203 offset += CHECKSUM_LEN as u64;
204 }
205 index
206 }
207
208 pub(crate) fn build_data(&self) -> Vec<u8> {
209 let mut data = vec![];
210 for entry in self.entries().values() {
211 let checksum = make_checksum(self.options.data_checksum_seed, entry.data());
212 data.extend_from_slice(entry.data());
213 data.extend_from_slice(&checksum.to_be_bytes());
214 }
215 data
216 }
217}