Skip to content

Commit

Permalink
Add cargo remove subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
cassaundra committed Sep 20, 2022
1 parent 1e48b75 commit 659a526
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/bin/cargo/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub fn builtin() -> Vec<App> {
pkgid::cli(),
publish::cli(),
read_manifest::cli(),
remove::cli(),
report::cli(),
run::cli(),
rustc::cli(),
Expand Down Expand Up @@ -68,6 +69,7 @@ pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches) -> CliResu
"pkgid" => pkgid::exec,
"publish" => publish::exec,
"read-manifest" => read_manifest::exec,
"remove" => remove::exec,
"report" => report::exec,
"run" => run::exec,
"rustc" => rustc::exec,
Expand Down Expand Up @@ -110,6 +112,7 @@ pub mod package;
pub mod pkgid;
pub mod publish;
pub mod read_manifest;
pub mod remove;
pub mod report;
pub mod run;
pub mod rustc;
Expand Down
116 changes: 116 additions & 0 deletions src/bin/cargo/commands/remove.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use cargo::core::dependency::DepKind;
use cargo::ops::cargo_remove::remove;
use cargo::ops::cargo_remove::RemoveOptions;
use cargo::ops::resolve_ws;
use cargo::util::command_prelude::*;
use cargo::util::toml_mut::manifest::DepTable;

pub fn cli() -> clap::Command<'static> {
clap::Command::new("remove")
.setting(clap::AppSettings::DeriveDisplayOrder)
.about("Remove dependencies from a Cargo.toml manifest file")
.args([clap::Arg::new("dependencies")
.action(clap::ArgAction::Append)
.required(true)
.multiple_values(true)
.takes_value(true)
.value_name("DEP_ID")
.help("Dependencies to be removed")])
.arg_package("Package to remove from")
.arg_manifest_path()
.arg_quiet()
.arg_dry_run("Don't actually write the manifest")
.next_help_heading("SECTION")
.args([
clap::Arg::new("dev")
.long("dev")
.conflicts_with("build")
.action(clap::ArgAction::SetTrue)
.group("section")
.help("Remove as development dependency"),
clap::Arg::new("build")
.long("build")
.conflicts_with("dev")
.action(clap::ArgAction::SetTrue)
.group("section")
.help("Remove as build dependency"),
clap::Arg::new("target")
.long("target")
.takes_value(true)
.value_name("TARGET")
.value_parser(clap::builder::NonEmptyStringValueParser::new())
.help("Remove as dependency from the given target platform"),
])
}

pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let dry_run = args.dry_run();

let workspace = args.workspace(config)?;
let packages = args.packages_from_flags()?;
let packages = packages.get_packages(&workspace)?;
let spec = match packages.len() {
0 => {
return Err(CliError::new(
anyhow::format_err!("no packages selected. Please specify one with `-p <PKG_ID>`"),
101,
));
}
1 => packages[0],
len => {
return Err(CliError::new(
anyhow::format_err!(
"{len} packages selected. Please specify one with `-p <PKG_ID>`",
),
101,
));
}
};

let dependencies = args
.get_many::<String>("dependencies")
.expect("required(true)")
.cloned()
.collect();

let section = parse_section(args);

let options = RemoveOptions {
config,
spec,
dependencies,
section,
dry_run,
};
remove(&options)?;

if !dry_run {
// Reload the workspace since we've changed dependencies
let ws = args.workspace(config)?;
resolve_ws(&ws)?;
}

Ok(())
}

fn parse_section(args: &ArgMatches) -> DepTable {
let dev = args.flag("dev");
let build = args.flag("build");

let kind = if dev {
DepKind::Development
} else if build {
DepKind::Build
} else {
DepKind::Normal
};

let mut table = DepTable::new().set_kind(kind);

if let Some(target) = args.get_one::<String>("target") {
assert!(!target.is_empty(), "Target specification may not be empty");
table = table.set_target(target);
}

table
}
65 changes: 65 additions & 0 deletions src/cargo/ops/cargo_remove.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//! Core of cargo-remove command
use crate::core::Package;
use crate::util::toml_mut::manifest::DepTable;
use crate::util::toml_mut::manifest::LocalManifest;
use crate::CargoResult;
use crate::Config;

/// Remove a dependency from a Cargo.toml manifest file.
#[derive(Debug)]
pub struct RemoveOptions<'a> {
/// Configuration information for Cargo operations
pub config: &'a Config,
/// Package to remove dependencies from
pub spec: &'a Package,
/// Dependencies to remove
pub dependencies: Vec<String>,
/// Which dependency section to remove these from
pub section: DepTable,
/// Whether or not to actually write the manifest
pub dry_run: bool,
}

/// Remove dependencies from a manifest
pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
let dep_table = options
.section
.to_table()
.into_iter()
.map(String::from)
.collect::<Vec<_>>();

let manifest_path = options.spec.manifest_path().to_path_buf();
let mut manifest = LocalManifest::try_new(&manifest_path)?;

for dep in &options.dependencies {
let section = if dep_table.len() >= 3 {
format!("{} for target `{}`", &dep_table[2], &dep_table[1])
} else {
dep_table[0].clone()
};
options
.config
.shell()
.status("Removing", format!("{dep} from {section}"))?;

manifest.remove_from_table(&dep_table, dep)?;

// Now that we have removed the crate, if that was the last reference to that
// crate, then we need to drop any explicitly activated features on
// that crate.
manifest.gc_dep(dep);
}

if options.dry_run {
options
.config
.shell()
.warn("aborting remove due to dry run")?;
} else {
manifest.write()?;
}

Ok(())
}
1 change: 1 addition & 0 deletions src/cargo/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ mod cargo_output_metadata;
mod cargo_package;
mod cargo_pkgid;
mod cargo_read_manifest;
pub mod cargo_remove;
mod cargo_run;
mod cargo_test;
mod cargo_uninstall;
Expand Down
1 change: 1 addition & 0 deletions tests/testsuite/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod cargo_command;
mod cargo_config;
mod cargo_env_config;
mod cargo_features;
mod cargo_remove;
mod cargo_targets;
mod cfg;
mod check;
Expand Down

0 comments on commit 659a526

Please sign in to comment.