warg_cli/commands/
dependencies.rs

1use super::CommonOptions;
2use anyhow::{bail, Result};
3use async_recursion::async_recursion;
4use clap::Args;
5use ptree::{output::print_tree, TreeBuilder};
6use std::default::Default;
7use std::fs;
8use warg_client::{
9    storage::PackageInfo,
10    version_util::{
11        create_child_node, new_tree, version_string, DependencyImportParser, ImportKind,
12    },
13    FileSystemClient,
14};
15use warg_protocol::{registry::PackageName, VersionReq};
16use wasmparser::{Chunk, ComponentImport, ComponentImportSectionReader, Parser, Payload};
17
18/// Print Dependency Tree
19#[derive(Args)]
20pub struct DependenciesCommand {
21    /// The common command options.
22    #[clap(flatten)]
23    pub common: CommonOptions,
24
25    /// Only show information for the specified package.
26    #[clap(value_name = "PACKAGE")]
27    pub package: PackageName,
28}
29
30impl DependenciesCommand {
31    /// Executes the command.
32    pub async fn exec(self) -> Result<()> {
33        let config = self.common.read_config()?;
34        let client = self.common.create_client(&config).await?;
35
36        let info = client.package(&self.package).await?;
37        Self::print_package_info(&client, &info).await?;
38
39        Ok(())
40    }
41
42    #[async_recursion]
43    async fn parse_deps<'a>(
44        id: &'a PackageName,
45        version: VersionReq,
46        client: &FileSystemClient,
47        node: &mut TreeBuilder,
48        parser: &mut DepsParser,
49    ) -> Result<()> {
50        client.download(id, &version).await?;
51
52        if let Some(download) = client.download(id, &version).await? {
53            let bytes = fs::read(download.path)?;
54            let deps = parser.parse(&bytes)?;
55            for dep in deps {
56                let mut dep_parser = DependencyImportParser {
57                    next: dep.name.0,
58                    offset: 0,
59                };
60                let dep = dep_parser.parse()?;
61                let v = version_string(&dep.req);
62                let grand_child = create_child_node(node, &dep.name, &v);
63                match dep.kind {
64                    ImportKind::Locked(_) | ImportKind::Unlocked => {
65                        let id = PackageName::new(dep.name)?;
66                        Self::parse_deps(&id, dep.req, client, grand_child, parser).await?;
67                    }
68                    ImportKind::Interface(_) => {}
69                }
70                grand_child.end_child();
71            }
72        }
73
74        Ok(())
75    }
76
77    async fn print_package_info(client: &FileSystemClient, info: &PackageInfo) -> Result<()> {
78        let mut parser = DepsParser::new();
79
80        if let Some(download) = client.download(&info.name, &Default::default()).await? {
81            let mut tree = new_tree(info.name.namespace(), info.name.name(), &download.version);
82            let bytes = fs::read(&download.path)?;
83            let deps = parser.parse(&bytes)?;
84            for dep in deps {
85                let mut dep_parser = DependencyImportParser {
86                    next: dep.name.0,
87                    offset: 0,
88                };
89                let dep = dep_parser.parse()?;
90                let v = version_string(&dep.req);
91                let child = create_child_node(&mut tree, &dep.name, &v);
92                match dep.kind {
93                    ImportKind::Locked(_) | ImportKind::Unlocked => {
94                        Self::parse_deps(
95                            &PackageName::new(dep.name)?,
96                            dep.req,
97                            client,
98                            child,
99                            &mut parser,
100                        )
101                        .await?;
102                    }
103                    ImportKind::Interface(_) => {}
104                }
105                child.end_child();
106            }
107            let built = tree.build();
108            print_tree(&built)?
109        }
110        Ok(())
111    }
112}
113
114struct DepsParser {}
115
116impl DepsParser {
117    pub fn new() -> Self {
118        Self {}
119    }
120
121    pub fn parse_imports<'a>(
122        &mut self,
123        parser: ComponentImportSectionReader<'a>,
124        deps: &mut Vec<ComponentImport<'a>>,
125    ) -> Result<()> {
126        for import in parser.into_iter_with_offsets() {
127            let (_, imp) = import?;
128            deps.push(imp);
129        }
130        Ok(())
131    }
132
133    pub fn parse<'a>(&mut self, mut bytes: &'a [u8]) -> Result<Vec<ComponentImport<'a>>> {
134        let mut parser = Parser::new(0);
135        let mut deps = Vec::new();
136        loop {
137            let payload = match parser.parse(bytes, true)? {
138                Chunk::NeedMoreData(_) => unreachable!(),
139                Chunk::Parsed { payload, consumed } => {
140                    bytes = &bytes[consumed..];
141                    payload
142                }
143            };
144            match payload {
145                Payload::ComponentImportSection(s) => {
146                    self.parse_imports(s, &mut deps)?;
147                }
148                Payload::CodeSectionStart { .. } => {
149                    parser.skip_section();
150                }
151                Payload::ModuleSection {
152                    unchecked_range, ..
153                } => {
154                    let offset = unchecked_range.end - unchecked_range.start;
155                    if offset > bytes.len() {
156                        bail!("invalid module or component section range");
157                    }
158                    bytes = &bytes[offset..];
159                }
160                Payload::ComponentSection {
161                    unchecked_range, ..
162                } => {
163                    let offset = unchecked_range.end - unchecked_range.start;
164                    if offset > bytes.len() {
165                        bail!("invalid module or component section range");
166                    }
167                    bytes = &bytes[offset..];
168                }
169                Payload::End(_) => {
170                    break;
171                }
172                _ => {}
173            }
174        }
175        Ok(deps)
176    }
177}