Skip to content

Commit aebfd9a

Browse files
committed
Add cargo remove subcommand
1 parent 7c218b1 commit aebfd9a

File tree

5 files changed

+187
-1
lines changed

5 files changed

+187
-1
lines changed

src/bin/cargo/commands/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub fn builtin() -> Vec<Command> {
2626
pkgid::cli(),
2727
publish::cli(),
2828
read_manifest::cli(),
29+
remove::cli(),
2930
report::cli(),
3031
run::cli(),
3132
rustc::cli(),
@@ -68,6 +69,7 @@ pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches) -> CliResu
6869
"pkgid" => pkgid::exec,
6970
"publish" => publish::exec,
7071
"read-manifest" => read_manifest::exec,
72+
"remove" => remove::exec,
7173
"report" => report::exec,
7274
"run" => run::exec,
7375
"rustc" => rustc::exec,
@@ -110,6 +112,7 @@ pub mod package;
110112
pub mod pkgid;
111113
pub mod publish;
112114
pub mod read_manifest;
115+
pub mod remove;
113116
pub mod report;
114117
pub mod run;
115118
pub mod rustc;

src/bin/cargo/commands/remove.rs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use cargo::core::dependency::DepKind;
2+
use cargo::ops::cargo_remove::remove;
3+
use cargo::ops::cargo_remove::RemoveOptions;
4+
use cargo::ops::resolve_ws;
5+
use cargo::util::command_prelude::*;
6+
use cargo::util::toml_mut::manifest::DepTable;
7+
8+
pub fn cli() -> clap::Command {
9+
clap::Command::new("remove")
10+
// Subcommand aliases are handled in `aliased_command()`.
11+
// .alias("rm")
12+
.about("Remove dependencies from a Cargo.toml manifest file")
13+
.args([clap::Arg::new("dependencies")
14+
.action(clap::ArgAction::Append)
15+
.required(true)
16+
.num_args(1..)
17+
.value_name("DEP_ID")
18+
.help("Dependencies to be removed")])
19+
.arg_package("Package to remove from")
20+
.arg_manifest_path()
21+
.arg_quiet()
22+
.arg_dry_run("Don't actually write the manifest")
23+
.next_help_heading("SECTION")
24+
.args([
25+
clap::Arg::new("dev")
26+
.long("dev")
27+
.conflicts_with("build")
28+
.action(clap::ArgAction::SetTrue)
29+
.group("section")
30+
.help("Remove as development dependency"),
31+
clap::Arg::new("build")
32+
.long("build")
33+
.conflicts_with("dev")
34+
.action(clap::ArgAction::SetTrue)
35+
.group("section")
36+
.help("Remove as build dependency"),
37+
clap::Arg::new("target")
38+
.long("target")
39+
.num_args(1)
40+
.value_name("TARGET")
41+
.value_parser(clap::builder::NonEmptyStringValueParser::new())
42+
.help("Remove as dependency from the given target platform"),
43+
])
44+
}
45+
46+
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
47+
let dry_run = args.dry_run();
48+
49+
let workspace = args.workspace(config)?;
50+
let packages = args.packages_from_flags()?;
51+
let packages = packages.get_packages(&workspace)?;
52+
let spec = match packages.len() {
53+
0 => {
54+
return Err(CliError::new(
55+
anyhow::format_err!("no packages selected. Please specify one with `-p <PKG_ID>`"),
56+
101,
57+
));
58+
}
59+
1 => packages[0],
60+
len => {
61+
return Err(CliError::new(
62+
anyhow::format_err!(
63+
"{len} packages selected. Please specify one with `-p <PKG_ID>`",
64+
),
65+
101,
66+
));
67+
}
68+
};
69+
70+
let dependencies = args
71+
.get_many::<String>("dependencies")
72+
.expect("required(true)")
73+
.cloned()
74+
.collect();
75+
76+
let section = parse_section(args);
77+
78+
let options = RemoveOptions {
79+
config,
80+
spec,
81+
dependencies,
82+
section,
83+
dry_run,
84+
};
85+
remove(&options)?;
86+
87+
if !dry_run {
88+
// Reload the workspace since we've changed dependencies
89+
let ws = args.workspace(config)?;
90+
resolve_ws(&ws)?;
91+
}
92+
93+
Ok(())
94+
}
95+
96+
fn parse_section(args: &ArgMatches) -> DepTable {
97+
let dev = args.flag("dev");
98+
let build = args.flag("build");
99+
100+
let kind = if dev {
101+
DepKind::Development
102+
} else if build {
103+
DepKind::Build
104+
} else {
105+
DepKind::Normal
106+
};
107+
108+
let mut table = DepTable::new().set_kind(kind);
109+
110+
if let Some(target) = args.get_one::<String>("target") {
111+
assert!(!target.is_empty(), "Target specification may not be empty");
112+
table = table.set_target(target);
113+
}
114+
115+
table
116+
}

src/bin/cargo/main.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@ fn main() {
3939

4040
/// Table for defining the aliases which come builtin in `Cargo`.
4141
/// The contents are structured as: `(alias, aliased_command, description)`.
42-
const BUILTIN_ALIASES: [(&str, &str, &str); 5] = [
42+
const BUILTIN_ALIASES: [(&str, &str, &str); 6] = [
4343
("b", "build", "alias: build"),
4444
("c", "check", "alias: check"),
4545
("d", "doc", "alias: doc"),
4646
("r", "run", "alias: run"),
4747
("t", "test", "alias: test"),
48+
("rm", "remove", "alias: remove"),
4849
];
4950

5051
/// Function which contains the list of all of the builtin aliases and it's

src/cargo/ops/cargo_remove.rs

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//! Core of cargo-remove command
2+
3+
use crate::core::Package;
4+
use crate::util::toml_mut::manifest::DepTable;
5+
use crate::util::toml_mut::manifest::LocalManifest;
6+
use crate::CargoResult;
7+
use crate::Config;
8+
9+
/// Remove a dependency from a Cargo.toml manifest file.
10+
#[derive(Debug)]
11+
pub struct RemoveOptions<'a> {
12+
/// Configuration information for Cargo operations
13+
pub config: &'a Config,
14+
/// Package to remove dependencies from
15+
pub spec: &'a Package,
16+
/// Dependencies to remove
17+
pub dependencies: Vec<String>,
18+
/// Which dependency section to remove these from
19+
pub section: DepTable,
20+
/// Whether or not to actually write the manifest
21+
pub dry_run: bool,
22+
}
23+
24+
/// Remove dependencies from a manifest
25+
pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
26+
let dep_table = options
27+
.section
28+
.to_table()
29+
.into_iter()
30+
.map(String::from)
31+
.collect::<Vec<_>>();
32+
33+
let manifest_path = options.spec.manifest_path().to_path_buf();
34+
let mut manifest = LocalManifest::try_new(&manifest_path)?;
35+
36+
for dep in &options.dependencies {
37+
let section = if dep_table.len() >= 3 {
38+
format!("{} for target `{}`", &dep_table[2], &dep_table[1])
39+
} else {
40+
dep_table[0].clone()
41+
};
42+
options
43+
.config
44+
.shell()
45+
.status("Removing", format!("{dep} from {section}"))?;
46+
47+
manifest.remove_from_table(&dep_table, dep)?;
48+
49+
// Now that we have removed the crate, if that was the last reference to that
50+
// crate, then we need to drop any explicitly activated features on
51+
// that crate.
52+
manifest.gc_dep(dep);
53+
}
54+
55+
if options.dry_run {
56+
options
57+
.config
58+
.shell()
59+
.warn("aborting remove due to dry run")?;
60+
} else {
61+
manifest.write()?;
62+
}
63+
64+
Ok(())
65+
}

src/cargo/ops/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ mod cargo_output_metadata;
4545
mod cargo_package;
4646
mod cargo_pkgid;
4747
mod cargo_read_manifest;
48+
pub mod cargo_remove;
4849
mod cargo_run;
4950
mod cargo_test;
5051
mod cargo_uninstall;

0 commit comments

Comments
 (0)