blob: c802e2af55d37a96ec29fd423ddaf635ec3fe087 [file] [edit]
//! Core of cargo-remove command
use crate::CargoResult;
use crate::GlobalContext;
use crate::workspace::Package;
use crate::workspace::editor::manifest::DepTable;
use crate::workspace::editor::manifest::LocalManifest;
use crate::workspace::editor::manifest::MissingDependencyError;
/// Remove a dependency from a Cargo.toml manifest file.
#[derive(Debug)]
pub struct RemoveOptions<'a> {
/// Configuration information for Cargo operations
pub gctx: &'a GlobalContext,
/// 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
.gctx
.shell()
.status("Removing", format!("{dep} from {section}"))?;
manifest.remove_from_table(&dep_table, dep).map_err(
|MissingDependencyError {
expected_name,
expected_path,
alt_name,
alt_path,
}| {
use std::fmt::Write as _;
let mut error = String::new();
let path = expected_path.join(".");
let _ = write!(
&mut error,
"the dependency `{expected_name}` could not be found in `{path}`"
);
if let Some(alt_path) = alt_path {
let mut flags = Vec::new();
match (
expected_path.last().unwrap().as_str(),
alt_path.last().unwrap().as_str(),
) {
("build-dependencies", "build-dependencies") => {}
("dev-dependencies", "dev-dependencies") => {}
("dependencies", "dependencies") => {}
(_, "build-dependencies") => flags.push("--build"),
(_, "dev-dependencies") => flags.push("--dev"),
(_, _) => {}
}
if expected_path[0] != "target" && alt_path[0] == "target" {
flags.push(&"--target");
flags.push(&alt_path[1]);
}
let alt_path = alt_path.join(".");
if !flags.is_empty() {
let flags = flags.join(" ");
let _ = write!(
&mut error,
"\n\nhelp: pass `{flags}` to remove `{expected_name}` from `{alt_path}`"
);
} else {
let _ = write!(
&mut error,
"\n\nhelp: a dependency with the same name exists in `{alt_path}`"
);
}
} else if let Some(alt_name) = alt_name {
let _ = write!(
&mut error,
"\n\nhelp: a dependency with a similar name exists: `{alt_name}`"
);
}
anyhow::format_err!("{error}")
},
)?;
// 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);
}
manifest.ensure_edition();
if options.dry_run {
options
.gctx
.shell()
.warn("aborting remove due to dry run")?;
} else {
manifest.write()?;
}
Ok(())
}