diff options
Diffstat (limited to 'src/cargo/ops/cargo_uninstall.rs')
-rw-r--r-- | src/cargo/ops/cargo_uninstall.rs | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/src/cargo/ops/cargo_uninstall.rs b/src/cargo/ops/cargo_uninstall.rs new file mode 100644 index 0000000..3551544 --- /dev/null +++ b/src/cargo/ops/cargo_uninstall.rs @@ -0,0 +1,155 @@ +use crate::core::PackageId; +use crate::core::{PackageIdSpec, SourceId}; +use crate::ops::common_for_install_and_uninstall::*; +use crate::sources::PathSource; +use crate::util::errors::CargoResult; +use crate::util::Config; +use crate::util::Filesystem; +use anyhow::bail; +use cargo_util::paths; +use std::collections::BTreeSet; +use std::env; + +pub fn uninstall( + root: Option<&str>, + specs: Vec<&str>, + bins: &[String], + config: &Config, +) -> CargoResult<()> { + if specs.len() > 1 && !bins.is_empty() { + bail!("A binary can only be associated with a single installed package, specifying multiple specs with --bin is redundant."); + } + + let root = resolve_root(root, config)?; + let scheduled_error = if specs.len() == 1 { + uninstall_one(&root, specs[0], bins, config)?; + false + } else if specs.is_empty() { + uninstall_cwd(&root, bins, config)?; + false + } else { + let mut succeeded = vec![]; + let mut failed = vec![]; + for spec in specs { + let root = root.clone(); + match uninstall_one(&root, spec, bins, config) { + Ok(()) => succeeded.push(spec), + Err(e) => { + crate::display_error(&e, &mut config.shell()); + failed.push(spec) + } + } + } + + let mut summary = vec![]; + if !succeeded.is_empty() { + summary.push(format!( + "Successfully uninstalled {}!", + succeeded.join(", ") + )); + } + if !failed.is_empty() { + summary.push(format!( + "Failed to uninstall {} (see error(s) above).", + failed.join(", ") + )); + } + + if !succeeded.is_empty() || !failed.is_empty() { + config.shell().status("Summary", summary.join(" "))?; + } + + !failed.is_empty() + }; + + if scheduled_error { + bail!("some packages failed to uninstall"); + } + + Ok(()) +} + +pub fn uninstall_one( + root: &Filesystem, + spec: &str, + bins: &[String], + config: &Config, +) -> CargoResult<()> { + let tracker = InstallTracker::load(config, root)?; + let all_pkgs = tracker.all_installed_bins().map(|(pkg_id, _set)| *pkg_id); + let pkgid = PackageIdSpec::query_str(spec, all_pkgs)?; + uninstall_pkgid(root, tracker, pkgid, bins, config) +} + +fn uninstall_cwd(root: &Filesystem, bins: &[String], config: &Config) -> CargoResult<()> { + let tracker = InstallTracker::load(config, root)?; + let source_id = SourceId::for_path(config.cwd())?; + let mut src = path_source(source_id, config)?; + let pkg = select_pkg( + &mut src, + None, + |path: &mut PathSource<'_>| path.read_packages(), + config, + )?; + let pkgid = pkg.package_id(); + uninstall_pkgid(root, tracker, pkgid, bins, config) +} + +fn uninstall_pkgid( + root: &Filesystem, + mut tracker: InstallTracker, + pkgid: PackageId, + bins: &[String], + config: &Config, +) -> CargoResult<()> { + let mut to_remove = Vec::new(); + let installed = match tracker.installed_bins(pkgid) { + Some(bins) => bins.clone(), + None => bail!("package `{}` is not installed", pkgid), + }; + + let dst = root.join("bin").into_path_unlocked(); + for bin in &installed { + let bin = dst.join(bin); + if !bin.exists() { + bail!( + "corrupt metadata, `{}` does not exist when it should", + bin.display() + ) + } + } + + let bins = bins + .iter() + .map(|s| { + if s.ends_with(env::consts::EXE_SUFFIX) { + s.to_string() + } else { + format!("{}{}", s, env::consts::EXE_SUFFIX) + } + }) + .collect::<BTreeSet<_>>(); + + for bin in bins.iter() { + if !installed.contains(bin) { + bail!("binary `{}` not installed as part of `{}`", bin, pkgid) + } + } + + if bins.is_empty() { + to_remove.extend(installed.iter().map(|b| dst.join(b))); + tracker.remove(pkgid, &installed); + } else { + for bin in bins.iter() { + to_remove.push(dst.join(bin)); + } + tracker.remove(pkgid, &bins); + } + tracker.save()?; + for bin in to_remove { + config.shell().status("Removing", bin.display())?; + paths::remove_file(bin)?; + } + + Ok(()) +} |