1#![allow(clippy::let_unit_value)]
6
7use crate::args::{
8 Args, Command, GcCommand, GetHashCommand, RepoAddCommand, RepoAddFileCommand,
9 RepoAddSubCommand, RepoAddUrlCommand, RepoCommand, RepoRemoveCommand, RepoShowCommand,
10 RepoSubCommand, ResolveCommand, RuleClearCommand, RuleCommand, RuleDumpDynamicCommand,
11 RuleListCommand, RuleReplaceCommand, RuleReplaceFileCommand, RuleReplaceJsonCommand,
12 RuleReplaceSubCommand, RuleSubCommand,
13};
14use anyhow::{Context as _, format_err};
15use fetch_url::fetch_url;
16use fidl_fuchsia_pkg as fpkg;
17use fidl_fuchsia_pkg_ext as pkg;
18use fidl_fuchsia_pkg_garbagecollector as fpkg_gc;
19use fidl_fuchsia_pkg_rewrite::EngineMarker;
20use fidl_fuchsia_pkg_rewrite_ext::{Rule as RewriteRule, RuleConfig, do_transaction};
21use fuchsia_async as fasync;
22use fuchsia_component::client::connect_to_protocol;
23use fuchsia_url::RepositoryUrl;
24use futures::stream::TryStreamExt;
25use std::fs::File;
26use std::io;
27use std::process::exit;
28
29mod args;
30
31pub fn main() -> Result<(), anyhow::Error> {
32 let mut executor = fasync::LocalExecutorBuilder::new().build();
33 let Args { command } = argh::from_env();
34 exit(executor.run_singlethreaded(main_helper(command))?)
35}
36
37async fn main_helper(command: Command) -> Result<i32, anyhow::Error> {
38 match command {
39 Command::Resolve(ResolveCommand { pkg_url, verbose }) => {
40 let resolver = connect_to_protocol::<fpkg::PackageResolverMarker>()
41 .context("Failed to connect to resolver service")?;
42 println!("resolving {pkg_url}");
43
44 let (dir, dir_server_end) = fidl::endpoints::create_proxy();
45
46 let _: fpkg::ResolutionContext = resolver
47 .resolve(&pkg_url, dir_server_end)
48 .await?
49 .map_err(fidl_fuchsia_pkg_ext::ResolveError::from)
50 .with_context(|| format!("Failed to resolve {pkg_url}"))?;
51
52 if verbose {
53 println!("package contents:");
54 let mut stream =
55 fuchsia_fs::directory::readdir_recursive(&dir, None);
56 while let Some(entry) = stream.try_next().await? {
57 println!("/{}", entry.name);
58 }
59 }
60
61 Ok(0)
62 }
63 Command::GetHash(GetHashCommand { pkg_url }) => {
64 let resolver = connect_to_protocol::<fpkg::PackageResolverMarker>()
65 .context("Failed to connect to resolver service")?;
66 let blob_id =
67 resolver.get_hash(&fpkg::PackageUrl { url: pkg_url }).await?.map_err(|i| {
68 format_err!(
69 "Failed to get package hash with error: {}",
70 zx::Status::err_from_raw(i)
71 )
72 })?;
73 println!("{}", pkg::BlobId::from(blob_id));
74 Ok(0)
75 }
76 Command::Repo(RepoCommand { verbose, subcommand }) => {
77 let repo_manager = connect_to_protocol::<fpkg::RepositoryManagerMarker>()
78 .context("Failed to connect to resolver service")?;
79
80 match subcommand {
81 None => {
82 if !verbose {
83 let repos = fetch_repos(repo_manager).await?;
85
86 let mut urls =
87 repos.into_iter().map(|r| r.repo_url().to_string()).collect::<Vec<_>>();
88 urls.sort_unstable();
89 urls.into_iter().for_each(|url| println!("{url}"));
90 } else {
91 let repos = fetch_repos(repo_manager).await?;
92
93 let s = serde_json::to_string_pretty(&repos).expect("valid json");
94 println!("{s}");
95 }
96 Ok(0)
97 }
98 Some(RepoSubCommand::Add(RepoAddCommand { subcommand })) => {
99 match subcommand {
100 RepoAddSubCommand::File(RepoAddFileCommand { persist, name, file }) => {
101 let mut repo: pkg::RepositoryConfig =
102 serde_json::from_reader(io::BufReader::new(File::open(file)?))?;
103 if let Some(n) = name {
106 repo = pkg::RepositoryConfigBuilder::from(repo)
107 .repo_url(RepositoryUrl::parse_host(n)?)
108 .build();
109 }
110 if persist {
113 repo = pkg::RepositoryConfigBuilder::from(repo)
114 .repo_storage_type(pkg::RepositoryStorageType::Persistent)
115 .build();
116 }
117
118 let res = repo_manager.add(&repo.into()).await?;
119 let () = res.map_err(zx::Status::err_from_raw)?;
120 }
121 RepoAddSubCommand::Url(RepoAddUrlCommand { persist, name, repo_url }) => {
122 let res = fetch_url(repo_url, None, vec![]).await?;
123 let mut repo: pkg::RepositoryConfig = serde_json::from_slice(&res)?;
124 if let Some(n) = name {
127 repo = pkg::RepositoryConfigBuilder::from(repo)
128 .repo_url(RepositoryUrl::parse_host(n)?)
129 .build();
130 }
131 if persist {
134 repo = pkg::RepositoryConfigBuilder::from(repo)
135 .repo_storage_type(pkg::RepositoryStorageType::Persistent)
136 .build();
137 }
138
139 let res = repo_manager.add(&repo.into()).await?;
140 let () = res.map_err(zx::Status::err_from_raw)?;
141 }
142 }
143
144 Ok(0)
145 }
146
147 Some(RepoSubCommand::Remove(RepoRemoveCommand { repo_url })) => {
148 let res = repo_manager.remove(&repo_url).await?;
149 let () = res.map_err(zx::Status::err_from_raw)?;
150
151 Ok(0)
152 }
153
154 Some(RepoSubCommand::Show(RepoShowCommand { repo_url })) => {
155 let repos = fetch_repos(repo_manager).await?;
156 for repo in repos.into_iter() {
157 if repo.repo_url().to_string() == repo_url {
158 let s = serde_json::to_string_pretty(&repo).expect("valid json");
159 println!("{s}");
160 return Ok(0);
161 }
162 }
163
164 println!("Package repository not found: {repo_url:?}");
165 Ok(1)
166 }
167 }
168 }
169 Command::Rule(RuleCommand { subcommand }) => {
170 let engine = connect_to_protocol::<EngineMarker>()
171 .context("Failed to connect to rewrite engine service")?;
172
173 match subcommand {
174 RuleSubCommand::List(RuleListCommand {}) => {
175 let (iter, iter_server_end) = fidl::endpoints::create_proxy();
176 engine.list(iter_server_end)?;
177
178 let mut rules = Vec::new();
179 loop {
180 let more = iter.next().await?;
181 if more.is_empty() {
182 break;
183 }
184 rules.extend(more);
185 }
186 let rules = rules.into_iter().map(|rule| rule.try_into()).collect::<Result<
187 Vec<RewriteRule>,
188 _,
189 >>(
190 )?;
191
192 for rule in rules {
193 println!("{rule:#?}");
194 }
195 }
196 RuleSubCommand::Clear(RuleClearCommand {}) => {
197 do_transaction(&engine, |transaction| async move {
198 transaction.reset_all()?;
199 Ok(transaction)
200 })
201 .await?;
202 }
203 RuleSubCommand::DumpDynamic(RuleDumpDynamicCommand {}) => {
204 let (transaction, transaction_server_end) = fidl::endpoints::create_proxy();
205 let () = engine.start_edit_transaction(transaction_server_end)?;
206 let (iter, iter_server_end) = fidl::endpoints::create_proxy();
207 transaction.list_dynamic(iter_server_end)?;
208 let mut rules = Vec::new();
209 loop {
210 let more = iter.next().await?;
211 if more.is_empty() {
212 break;
213 }
214 rules.extend(more);
215 }
216 let rules = rules.into_iter().map(|rule| rule.try_into()).collect::<Result<
217 Vec<RewriteRule>,
218 _,
219 >>(
220 )?;
221 let rule_configs = RuleConfig::Version1(rules);
222 let dynamic_rules = serde_json::to_string_pretty(&rule_configs)?;
223 println!("{dynamic_rules}");
224 }
225 RuleSubCommand::Replace(RuleReplaceCommand { subcommand }) => {
226 let RuleConfig::Version1(ref rules) = match subcommand {
227 RuleReplaceSubCommand::File(RuleReplaceFileCommand { file }) => {
228 serde_json::from_reader(io::BufReader::new(File::open(file)?))?
229 }
230 RuleReplaceSubCommand::Json(RuleReplaceJsonCommand { config }) => config,
231 };
232
233 do_transaction(&engine, |transaction| {
234 async move {
235 transaction.reset_all()?;
236 for rule in rules.iter().rev() {
239 let () = transaction.add(rule.clone()).await?;
240 }
241 Ok(transaction)
242 }
243 })
244 .await?;
245 }
246 }
247
248 Ok(0)
249 }
250 Command::Gc(GcCommand {}) => {
251 let space_manager = connect_to_protocol::<fpkg_gc::ManagerMarker>()
252 .context("Failed to connect to space manager service")?;
253 space_manager
254 .gc()
255 .await?
256 .map_err(|err| format_err!("Garbage collection failed with error: {:?}", err))
257 .map(|_| 0i32)
258 }
259 }
260}
261
262async fn fetch_repos(
263 repo_manager: fpkg::RepositoryManagerProxy,
264) -> Result<Vec<pkg::RepositoryConfig>, anyhow::Error> {
265 let (iter, server_end) = fidl::endpoints::create_proxy();
266 repo_manager.list(server_end)?;
267 let mut repos = vec![];
268
269 loop {
270 let chunk = iter.next().await?;
271 if chunk.is_empty() {
272 break;
273 }
274 repos.extend(chunk);
275 }
276
277 repos
278 .into_iter()
279 .map(|repo| pkg::RepositoryConfig::try_from(repo).map_err(anyhow::Error::from))
280 .collect()
281}