| //! 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(()) |
| } |