1use anyhow::{bail, Result};
2use async_recursion::async_recursion;
3use indexmap::IndexSet;
4use semver::VersionReq;
5use std::fs;
6use warg_protocol::package::{Release, ReleaseState};
7use warg_protocol::registry::PackageName;
8use wasm_encoder::{
9 Component, ComponentImportSection, ComponentSectionId, ComponentTypeRef, RawSection,
10};
11use wasmparser::{Chunk, ComponentImportSectionReader, Parser, Payload};
12
13use super::Client;
14use crate::storage::{ContentStorage, NamespaceMapStorage, PackageInfo, RegistryStorage};
15use crate::version_util::{DependencyImportParser, Import, ImportKind};
16pub struct LockListBuilder {
20 pub lock_list: IndexSet<Import>,
22}
23
24impl Default for LockListBuilder {
25 fn default() -> Self {
27 Self {
28 lock_list: IndexSet::new(),
29 }
30 }
31}
32
33impl LockListBuilder {
34 fn parse_import(
35 &self,
36 parser: &ComponentImportSectionReader,
37 imports: &mut Vec<String>,
38 ) -> Result<()> {
39 let clone = parser.clone();
40 for import in clone.into_iter_with_offsets() {
41 let (_, imp) = import?;
42 imports.push(imp.name.0.to_string());
43 }
44 Ok(())
45 }
46
47 #[async_recursion]
48 async fn parse_package<R, C, N>(
49 &mut self,
50 client: &Client<R, C, N>,
51 mut bytes: &[u8],
52 ) -> Result<()>
53 where
54 R: RegistryStorage,
55 C: ContentStorage,
56 N: NamespaceMapStorage,
57 {
58 let mut parser = Parser::new(0);
59 let mut imports: Vec<String> = Vec::new();
60 loop {
61 let payload = match parser.parse(bytes, true)? {
62 Chunk::NeedMoreData(_) => unreachable!(),
63 Chunk::Parsed { payload, consumed } => {
64 bytes = &bytes[consumed..];
65 payload
66 }
67 };
68 match payload {
69 Payload::ComponentImportSection(s) => {
70 self.parse_import(&s, &mut imports)?;
71 }
72 Payload::CodeSectionStart {
73 count: _,
74 range: _,
75 size: _,
76 } => {
77 parser.skip_section();
78 }
79 Payload::ModuleSection {
80 unchecked_range, ..
81 } => {
82 let offset = unchecked_range.end - unchecked_range.start;
83 if offset > bytes.len() {
84 bail!("invalid module or component section range");
85 }
86 bytes = &bytes[offset..];
87 }
88 Payload::ComponentSection {
89 unchecked_range, ..
90 } => {
91 let offset = unchecked_range.end - unchecked_range.start;
92 if offset > bytes.len() {
93 bail!("invalid module or component section range");
94 }
95 bytes = &bytes[offset..];
96 }
97 Payload::End(_) => {
98 break;
99 }
100 _ => {}
101 }
102 }
103 for import in imports {
104 let mut resolver = DependencyImportParser {
105 next: &import,
106 offset: 0,
107 };
108
109 let import = resolver.parse()?;
110 match import.kind {
111 ImportKind::Locked(_) | ImportKind::Unlocked => {
112 let id = PackageName::new(import.name.clone())?;
113 let registry_domain = client.get_warg_registry(id.namespace()).await?;
114 if let Some(info) = client
115 .registry()
116 .load_package(registry_domain.as_ref(), &id)
117 .await?
118 {
119 let release = info.state.releases().last();
120 if let Some(r) = release {
121 if let Some(bytes) = self.release_bytes(r, client)? {
122 self.parse_package(client, &bytes).await?;
123 }
124 }
125 self.lock_list.insert(import);
126 } else {
127 client.download(&id, &VersionReq::STAR).await?;
128 if let Some(info) = client
129 .registry()
130 .load_package(
131 client.get_warg_registry(id.namespace()).await?.as_ref(),
132 &id,
133 )
134 .await?
135 {
136 let release = info.state.releases().last();
137 if let Some(r) = release {
138 if let Some(bytes) = self.release_bytes(r, client)? {
139 self.parse_package(client, &bytes).await?;
140 }
141 }
142 self.lock_list.insert(import);
143 }
144 }
145 }
146 ImportKind::Interface(_) => {}
147 }
148 }
149 Ok(())
150 }
151
152 fn release_bytes<R: RegistryStorage, C: ContentStorage, N: NamespaceMapStorage>(
153 &self,
154 release: &Release,
155 client: &Client<R, C, N>,
156 ) -> Result<Option<Vec<u8>>> {
157 let state = &release.state;
158 if let ReleaseState::Released { content } = state {
159 let path = client.content().content_location(content);
160 if let Some(p) = path {
161 return Ok(Some(fs::read(p)?));
162 }
163 }
164 Ok(None)
165 }
166
167 #[async_recursion]
169 pub async fn build_list<R, C, N>(
170 &mut self,
171 client: &Client<R, C, N>,
172 info: &PackageInfo,
173 ) -> Result<()>
174 where
175 R: RegistryStorage,
176 C: ContentStorage,
177 N: NamespaceMapStorage,
178 {
179 let release = info.state.releases().last();
180 if let Some(r) = release {
181 let state = &r.state;
182 if let ReleaseState::Released { content } = state {
183 let path = client.content().content_location(content);
184 if let Some(p) = path {
185 let bytes = fs::read(p)?;
186 self.parse_package(client, &bytes).await?;
187 }
188 }
189 }
190 Ok(())
191 }
192}
193
194pub struct Bundler<'a, R, C, N>
196where
197 R: RegistryStorage,
198 C: ContentStorage,
199 N: NamespaceMapStorage,
200{
201 client: &'a Client<R, C, N>,
203}
204
205impl<'a, R, C, N> Bundler<'a, R, C, N>
206where
207 R: RegistryStorage,
208 C: ContentStorage,
209 N: NamespaceMapStorage,
210{
211 pub fn new(client: &'a Client<R, C, N>) -> Self {
213 Self { client }
214 }
215
216 async fn parse_imports(
217 &mut self,
218 parser: ComponentImportSectionReader<'a>,
219 component: &mut Component,
220 ) -> Result<Vec<u8>> {
221 let mut imports = ComponentImportSection::new();
222 for import in parser.into_iter_with_offsets() {
223 let (_, imp) = import?;
224 let mut dep_parser = DependencyImportParser {
225 next: imp.name.0,
226 offset: 0,
227 };
228 let parsed_imp = dep_parser.parse()?;
229 if !parsed_imp.name.contains('/') {
230 let pkg_id = PackageName::new(parsed_imp.name)?;
231 if let Some(info) = self
232 .client
233 .registry()
234 .load_package(
235 self.client
236 .get_warg_registry(pkg_id.namespace())
237 .await?
238 .as_ref(),
239 &pkg_id,
240 )
241 .await?
242 {
243 let release = if parsed_imp.req != VersionReq::STAR {
244 info.state
245 .releases()
246 .filter(|r| parsed_imp.req.matches(&r.version))
247 .last()
248 } else {
249 info.state.releases().last()
250 };
251 if let Some(r) = release {
252 let release_state = &r.state;
253 if let ReleaseState::Released { content } = release_state {
254 let path = self.client.content().content_location(content);
255 if let Some(p) = path {
256 let bytes = fs::read(p)?;
257 component.section(&RawSection {
258 id: ComponentSectionId::Component.into(),
259 data: &bytes,
260 });
261 }
262 }
263 }
264 }
265 } else if let wasmparser::ComponentTypeRef::Instance(i) = imp.ty {
266 imports.import(imp.name.0, ComponentTypeRef::Instance(i));
267 }
268 }
269 component.section(&imports);
270 Ok(Vec::new())
271 }
272
273 pub async fn parse(&mut self, mut bytes: &'a [u8]) -> Result<Component> {
275 let constant = bytes;
276 let mut parser = Parser::new(0);
277 let mut component = Component::new();
278 loop {
279 let payload = match parser.parse(bytes, true)? {
280 Chunk::NeedMoreData(_) => unreachable!(),
281 Chunk::Parsed { payload, consumed } => {
282 bytes = &bytes[consumed..];
283 payload
284 }
285 };
286 match payload {
287 Payload::ComponentImportSection(s) => {
288 self.parse_imports(s, &mut component).await?;
289 }
290 Payload::ModuleSection {
291 unchecked_range, ..
292 } => {
293 let offset = unchecked_range.end - unchecked_range.start;
294 component.section(&RawSection {
295 id: 1,
296 data: &constant[unchecked_range],
297 });
298 if offset > bytes.len() {
299 panic!();
300 }
301 bytes = &bytes[offset..];
302 }
303 Payload::End(_) => {
304 break;
305 }
306 _ => {
307 if let Some((id, range)) = payload.as_section() {
308 component.section(&RawSection {
309 id,
310 data: &constant[range],
311 });
312 }
313 }
314 }
315 }
316 Ok(component)
317 }
318}