warg_cli/commands/
dependencies.rs1use 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#[derive(Args)]
20pub struct DependenciesCommand {
21 #[clap(flatten)]
23 pub common: CommonOptions,
24
25 #[clap(value_name = "PACKAGE")]
27 pub package: PackageName,
28}
29
30impl DependenciesCommand {
31 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}