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 { range, .. } => {
80                    let offset = range.end - range.start;
81                    if offset > bytes.len() {
82                        bail!("invalid module or component section range");
83                    }
84                    bytes = &bytes[offset..];
85                }
86                Payload::ComponentSection { range, .. } => {
87                    let offset = range.end - range.start;
88                    if offset > bytes.len() {
89                        bail!("invalid module or component section range");
90                    }
91                    bytes = &bytes[offset..];
92                }
93                Payload::End(_) => {
94                    break;
95                }
96                _ => {}
97            }
98        }
99        for import in imports {
100            let mut resolver = DependencyImportParser {
101                next: &import,
102                offset: 0,
103            };
104
105            let import = resolver.parse()?;
106            match import.kind {
107                ImportKind::Locked(_) | ImportKind::Unlocked => {
108                    let id = PackageName::new(import.name.clone())?;
109                    let registry_domain = client.get_warg_registry(id.namespace()).await?;
110                    if let Some(info) = client
111                        .registry()
112                        .load_package(registry_domain.as_ref(), &id)
113                        .await?
114                    {
115                        let release = info.state.releases().last();
116                        if let Some(r) = release {
117                            if let Some(bytes) = self.release_bytes(r, client)? {
118                                self.parse_package(client, &bytes).await?;
119                            }
120                        }
121                        self.lock_list.insert(import);
122                    } else {
123                        client.download(&id, &VersionReq::STAR).await?;
124                        if let Some(info) = client
125                            .registry()
126                            .load_package(
127                                client.get_warg_registry(id.namespace()).await?.as_ref(),
128                                &id,
129                            )
130                            .await?
131                        {
132                            let release = info.state.releases().last();
133                            if let Some(r) = release {
134                                if let Some(bytes) = self.release_bytes(r, client)? {
135                                    self.parse_package(client, &bytes).await?;
136                                }
137                            }
138                            self.lock_list.insert(import);
139                        }
140                    }
141                }
142                ImportKind::Interface(_) => {}
143            }
144        }
145        Ok(())
146    }
147
148    fn release_bytes<R: RegistryStorage, C: ContentStorage, N: NamespaceMapStorage>(
149        &self,
150        release: &Release,
151        client: &Client<R, C, N>,
152    ) -> Result<Option<Vec<u8>>> {
153        let state = &release.state;
154        if let ReleaseState::Released { content } = state {
155            let path = client.content().content_location(content);
156            if let Some(p) = path {
157                return Ok(Some(fs::read(p)?));
158            }
159        }
160        Ok(None)
161    }
162
163    /// List of deps for building
164    #[async_recursion]
165    pub async fn build_list<R, C, N>(
166        &mut self,
167        client: &Client<R, C, N>,
168        info: &PackageInfo,
169    ) -> Result<()>
170    where
171        R: RegistryStorage,
172        C: ContentStorage,
173        N: NamespaceMapStorage,
174    {
175        let release = info.state.releases().last();
176        if let Some(r) = release {
177            let state = &r.state;
178            if let ReleaseState::Released { content } = state {
179                let path = client.content().content_location(content);
180                if let Some(p) = path {
181                    let bytes = fs::read(p)?;
182                    self.parse_package(client, &bytes).await?;
183                }
184            }
185        }
186        Ok(())
187    }
188}
189
190/// Bundles Dependencies
191pub struct Bundler<'a, R, C, N>
192where
193    R: RegistryStorage,
194    C: ContentStorage,
195    N: NamespaceMapStorage,
196{
197    /// Warg client used for bundling
198    client: &'a Client<R, C, N>,
199}
200
201impl<'a, R, C, N> Bundler<'a, R, C, N>
202where
203    R: RegistryStorage,
204    C: ContentStorage,
205    N: NamespaceMapStorage,
206{
207    /// New Bundler
208    pub fn new(client: &'a Client<R, C, N>) -> Self {
209        Self { client }
210    }
211
212    async fn parse_imports(
213        &mut self,
214        parser: ComponentImportSectionReader<'a>,
215        component: &mut Component,
216    ) -> Result<Vec<u8>> {
217        let mut imports = ComponentImportSection::new();
218        for import in parser.into_iter_with_offsets() {
219            let (_, imp) = import?;
220            let mut dep_parser = DependencyImportParser {
221                next: imp.name.0,
222                offset: 0,
223            };
224            let parsed_imp = dep_parser.parse()?;
225            if !parsed_imp.name.contains('/') {
226                let pkg_id = PackageName::new(parsed_imp.name)?;
227                if let Some(info) = self
228                    .client
229                    .registry()
230                    .load_package(
231                        self.client
232                            .get_warg_registry(pkg_id.namespace())
233                            .await?
234                            .as_ref(),
235                        &pkg_id,
236                    )
237                    .await?
238                {
239                    let release = if parsed_imp.req != VersionReq::STAR {
240                        info.state
241                            .releases()
242                            .filter(|r| parsed_imp.req.matches(&r.version))
243                            .last()
244                    } else {
245                        info.state.releases().last()
246                    };
247                    if let Some(r) = release {
248                        let release_state = &r.state;
249                        if let ReleaseState::Released { content } = release_state {
250                            let path = self.client.content().content_location(content);
251                            if let Some(p) = path {
252                                let bytes = fs::read(p)?;
253                                component.section(&RawSection {
254                                    id: ComponentSectionId::Component.into(),
255                                    data: &bytes,
256                                });
257                            }
258                        }
259                    }
260                }
261            } else if let wasmparser::ComponentTypeRef::Instance(i) = imp.ty {
262                imports.import(imp.name.0, ComponentTypeRef::Instance(i));
263            }
264        }
265        component.section(&imports);
266        Ok(Vec::new())
267    }
268
269    /// Parse bytes for bundling
270    pub async fn parse(&mut self, mut bytes: &'a [u8]) -> Result<Component> {
271        let constant = bytes;
272        let mut parser = Parser::new(0);
273        let mut component = Component::new();
274        loop {
275            let payload = match parser.parse(bytes, true)? {
276                Chunk::NeedMoreData(_) => unreachable!(),
277                Chunk::Parsed { payload, consumed } => {
278                    bytes = &bytes[consumed..];
279                    payload
280                }
281            };
282            match payload {
283                Payload::ComponentImportSection(s) => {
284                    self.parse_imports(s, &mut component).await?;
285                }
286                Payload::ModuleSection { range, .. } => {
287                    let offset = range.end - range.start;
288                    component.section(&RawSection {
289                        id: 1,
290                        data: &constant[range],
291                    });
292                    if offset > bytes.len() {
293                        panic!();
294                    }
295                    bytes = &bytes[offset..];
296                }
297                Payload::End(_) => {
298                    break;
299                }
300                _ => {
301                    if let Some((id, range)) = payload.as_section() {
302                        component.section(&RawSection {
303                            id,
304                            data: &constant[range],
305                        });
306                    }
307                }
308            }
309        }
310        Ok(component)
311    }
312}