]> git.proxmox.com Git - cargo.git/blob - src/cargo/ops/cargo_uninstall.rs
Print environment note for json format, too.
[cargo.git] / src / cargo / ops / cargo_uninstall.rs
1 use crate::core::PackageId;
2 use crate::core::{PackageIdSpec, SourceId};
3 use crate::ops::common_for_install_and_uninstall::*;
4 use crate::sources::PathSource;
5 use crate::util::errors::CargoResult;
6 use crate::util::Config;
7 use crate::util::Filesystem;
8 use anyhow::bail;
9 use cargo_util::paths;
10 use std::collections::BTreeSet;
11 use std::env;
12
13 pub fn uninstall(
14 root: Option<&str>,
15 specs: Vec<&str>,
16 bins: &[String],
17 config: &Config,
18 ) -> CargoResult<()> {
19 if specs.len() > 1 && !bins.is_empty() {
20 bail!("A binary can only be associated with a single installed package, specifying multiple specs with --bin is redundant.");
21 }
22
23 let root = resolve_root(root, config)?;
24 let scheduled_error = if specs.len() == 1 {
25 uninstall_one(&root, specs[0], bins, config)?;
26 false
27 } else if specs.is_empty() {
28 uninstall_cwd(&root, bins, config)?;
29 false
30 } else {
31 let mut succeeded = vec![];
32 let mut failed = vec![];
33 for spec in specs {
34 let root = root.clone();
35 match uninstall_one(&root, spec, bins, config) {
36 Ok(()) => succeeded.push(spec),
37 Err(e) => {
38 crate::display_error(&e, &mut config.shell());
39 failed.push(spec)
40 }
41 }
42 }
43
44 let mut summary = vec![];
45 if !succeeded.is_empty() {
46 summary.push(format!(
47 "Successfully uninstalled {}!",
48 succeeded.join(", ")
49 ));
50 }
51 if !failed.is_empty() {
52 summary.push(format!(
53 "Failed to uninstall {} (see error(s) above).",
54 failed.join(", ")
55 ));
56 }
57
58 if !succeeded.is_empty() || !failed.is_empty() {
59 config.shell().status("Summary", summary.join(" "))?;
60 }
61
62 !failed.is_empty()
63 };
64
65 if scheduled_error {
66 bail!("some packages failed to uninstall");
67 }
68
69 Ok(())
70 }
71
72 pub fn uninstall_one(
73 root: &Filesystem,
74 spec: &str,
75 bins: &[String],
76 config: &Config,
77 ) -> CargoResult<()> {
78 let tracker = InstallTracker::load(config, root)?;
79 let all_pkgs = tracker.all_installed_bins().map(|(pkg_id, _set)| *pkg_id);
80 let pkgid = PackageIdSpec::query_str(spec, all_pkgs)?;
81 uninstall_pkgid(root, tracker, pkgid, bins, config)
82 }
83
84 fn uninstall_cwd(root: &Filesystem, bins: &[String], config: &Config) -> CargoResult<()> {
85 let tracker = InstallTracker::load(config, root)?;
86 let source_id = SourceId::for_path(config.cwd())?;
87 let mut src = path_source(source_id, config)?;
88 let pkg = select_pkg(
89 &mut src,
90 None,
91 |path: &mut PathSource<'_>| path.read_packages(),
92 config,
93 )?;
94 let pkgid = pkg.package_id();
95 uninstall_pkgid(root, tracker, pkgid, bins, config)
96 }
97
98 fn uninstall_pkgid(
99 root: &Filesystem,
100 mut tracker: InstallTracker,
101 pkgid: PackageId,
102 bins: &[String],
103 config: &Config,
104 ) -> CargoResult<()> {
105 let mut to_remove = Vec::new();
106 let installed = match tracker.installed_bins(pkgid) {
107 Some(bins) => bins.clone(),
108 None => bail!("package `{}` is not installed", pkgid),
109 };
110
111 let dst = root.join("bin").into_path_unlocked();
112 for bin in &installed {
113 let bin = dst.join(bin);
114 if !bin.exists() {
115 bail!(
116 "corrupt metadata, `{}` does not exist when it should",
117 bin.display()
118 )
119 }
120 }
121
122 let bins = bins
123 .iter()
124 .map(|s| {
125 if s.ends_with(env::consts::EXE_SUFFIX) {
126 s.to_string()
127 } else {
128 format!("{}{}", s, env::consts::EXE_SUFFIX)
129 }
130 })
131 .collect::<BTreeSet<_>>();
132
133 for bin in bins.iter() {
134 if !installed.contains(bin) {
135 bail!("binary `{}` not installed as part of `{}`", bin, pkgid)
136 }
137 }
138
139 if bins.is_empty() {
140 to_remove.extend(installed.iter().map(|b| dst.join(b)));
141 tracker.remove(pkgid, &installed);
142 } else {
143 for bin in bins.iter() {
144 to_remove.push(dst.join(bin));
145 }
146 tracker.remove(pkgid, &bins);
147 }
148 tracker.save()?;
149 for bin in to_remove {
150 config.shell().status("Removing", bin.display())?;
151 paths::remove_file(bin)?;
152 }
153
154 Ok(())
155 }