Skip to main content

cargo/ops/
cargo_remove.rs

1//! Core of cargo-remove command
2
3use crate::CargoResult;
4use crate::GlobalContext;
5use crate::core::Package;
6use crate::util::toml_mut::manifest::DepTable;
7use crate::util::toml_mut::manifest::LocalManifest;
8use crate::util::toml_mut::manifest::MissingDependencyError;
9
10/// Remove a dependency from a Cargo.toml manifest file.
11#[derive(Debug)]
12pub struct RemoveOptions<'a> {
13    /// Configuration information for Cargo operations
14    pub gctx: &'a GlobalContext,
15    /// Package to remove dependencies from
16    pub spec: &'a Package,
17    /// Dependencies to remove
18    pub dependencies: Vec<String>,
19    /// Which dependency section to remove these from
20    pub section: DepTable,
21    /// Whether or not to actually write the manifest
22    pub dry_run: bool,
23}
24
25/// Remove dependencies from a manifest
26pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
27    let dep_table = options
28        .section
29        .to_table()
30        .into_iter()
31        .map(String::from)
32        .collect::<Vec<_>>();
33
34    let manifest_path = options.spec.manifest_path().to_path_buf();
35    let mut manifest = LocalManifest::try_new(&manifest_path)?;
36
37    for dep in &options.dependencies {
38        let section = if dep_table.len() >= 3 {
39            format!("{} for target `{}`", &dep_table[2], &dep_table[1])
40        } else {
41            dep_table[0].clone()
42        };
43        options
44            .gctx
45            .shell()
46            .status("Removing", format!("{dep} from {section}"))?;
47
48        manifest.remove_from_table(&dep_table, dep).map_err(
49            |MissingDependencyError {
50                 expected_name,
51                 expected_path,
52                 alt_name,
53                 alt_path,
54             }| {
55                use std::fmt::Write as _;
56
57                let mut error = String::new();
58                let path = expected_path.join(".");
59                let _ = write!(
60                    &mut error,
61                    "the dependency `{expected_name}` could not be found in `{path}`"
62                );
63                if let Some(alt_path) = alt_path {
64                    let mut flags = Vec::new();
65                    match (
66                        expected_path.last().unwrap().as_str(),
67                        alt_path.last().unwrap().as_str(),
68                    ) {
69                        ("build-dependencies", "build-dependencies") => {}
70                        ("dev-dependencies", "dev-dependencies") => {}
71                        ("dependencies", "dependencies") => {}
72                        (_, "build-dependencies") => flags.push("--build"),
73                        (_, "dev-dependencies") => flags.push("--dev"),
74                        (_, _) => {}
75                    }
76                    if expected_path[0] != "target" && alt_path[0] == "target" {
77                        flags.push(&"--target");
78                        flags.push(&alt_path[1]);
79                    }
80                    let alt_path = alt_path.join(".");
81                    if !flags.is_empty() {
82                        let flags = flags.join(" ");
83                        let _ = write!(
84                            &mut error,
85                            "\n\nhelp: pass `{flags}` to remove `{expected_name}` from `{alt_path}`"
86                        );
87                    } else {
88                        let _ = write!(
89                            &mut error,
90                            "\n\nhelp: a dependency with the same name exists in `{alt_path}`"
91                        );
92                    }
93                } else if let Some(alt_name) = alt_name {
94                    let _ = write!(
95                        &mut error,
96                        "\n\nhelp: a dependency with a similar name exists: `{alt_name}`"
97                    );
98                }
99                anyhow::format_err!("{error}")
100            },
101        )?;
102
103        // Now that we have removed the crate, if that was the last reference to that
104        // crate, then we need to drop any explicitly activated features on
105        // that crate.
106        manifest.gc_dep(dep);
107    }
108
109    if options.dry_run {
110        options
111            .gctx
112            .shell()
113            .warn("aborting remove due to dry run")?;
114    } else {
115        manifest.write()?;
116    }
117
118    Ok(())
119}