warg_client/
depsolve.rs

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};
16/// Import Kinds found in components
17
18/// Creates list of dependenies for locking components
19pub struct LockListBuilder {
20    /// List of deps to include in locked component
21    pub lock_list: IndexSet<Import>,
22}
23
24impl Default for LockListBuilder {
25    /// New LockListBuilder
26    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    /// List of deps for building
168    #[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
194/// Bundles Dependencies
195pub struct Bundler<'a, R, C, N>
196where
197    R: RegistryStorage,
198    C: ContentStorage,
199    N: NamespaceMapStorage,
200{
201    /// Warg client used for bundling
202    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    /// New Bundler
212    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    /// Parse bytes for bundling
274    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}