xtask/cmd/
workspace_deps.rs1use std::collections::{HashMap, HashSet};
2
3use anyhow::Context;
4use cargo_metadata::DependencyKind;
5
6use crate::cmd::IGNORED_PACKAGES;
7
8#[derive(Debug, Clone, clap::Parser)]
9pub struct WorkspaceDeps {
10 #[clap(long, short, value_delimiter = ',')]
11 #[clap(alias = "package")]
12 packages: Vec<String>,
14 #[clap(long, short, value_delimiter = ',')]
15 #[clap(alias = "exclude-package")]
16 exclude_packages: Vec<String>,
18}
19
20impl WorkspaceDeps {
21 pub fn run(self) -> anyhow::Result<()> {
22 let start = std::time::Instant::now();
23
24 let metadata = crate::utils::metadata()?;
25
26 let workspace_package_ids = metadata.workspace_members.iter().cloned().collect::<HashSet<_>>();
27
28 let workspace_packages = metadata
29 .packages
30 .iter()
31 .filter(|p| workspace_package_ids.contains(&p.id))
32 .map(|p| (&p.id, p))
33 .collect::<HashMap<_, _>>();
34
35 let path_to_package = workspace_packages
36 .values()
37 .map(|p| (p.manifest_path.parent().unwrap(), &p.id))
38 .collect::<HashMap<_, _>>();
39
40 for package in metadata.packages.iter().filter(|p| workspace_package_ids.contains(&p.id)) {
41 if (IGNORED_PACKAGES.contains(&package.name.as_str()) || self.exclude_packages.contains(&package.name))
42 && (self.packages.is_empty() || !self.packages.contains(&package.name))
43 {
44 continue;
45 }
46
47 let toml = std::fs::read_to_string(&package.manifest_path)
48 .with_context(|| format!("failed to read manifest for {}", package.name))?;
49 let mut doc = toml
50 .parse::<toml_edit::DocumentMut>()
51 .with_context(|| format!("failed to parse manifest for {}", package.name))?;
52 let mut changes = false;
53
54 for dependency in package
55 .dependencies
56 .iter()
57 .filter(|dep| dep.kind == DependencyKind::Development)
58 {
59 let Some(path) = dependency.path.as_deref() else {
60 continue;
61 };
62
63 if path_to_package.get(path).and_then(|id| workspace_packages.get(id)).is_none() {
64 continue;
65 }
66
67 doc["dev-dependencies"].as_table_mut().unwrap().remove(&dependency.name);
68 println!("Removed dev-dependency `{}` in package `{}`", dependency.name, package.name);
69 changes = true;
70 }
71
72 if changes {
73 std::fs::write(&package.manifest_path, doc.to_string())
74 .with_context(|| format!("failed to write manifest for {}", package.name))?;
75 println!("Removed dev-dependencies in {} @ {}", package.name, package.manifest_path);
76 }
77 }
78
79 println!("Done in {:?}", start.elapsed());
80
81 Ok(())
82 }
83}