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