Skip to content

Commit 0ab79d7

Browse files
committed
Deduplicate entries in cargo --list
Fixes #6088
1 parent 0a2581d commit 0ab79d7

File tree

4 files changed

+53
-42
lines changed

4 files changed

+53
-42
lines changed

src/bin/cargo/cli.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,21 +98,21 @@ Run with 'cargo -Z [FLAG] [SUBCOMMAND]'",
9898

9999
if args.is_present("list") {
100100
drop_println!(config, "Installed Commands:");
101-
for command in list_commands(config) {
101+
for (name, command) in list_commands(config) {
102102
match command {
103-
CommandInfo::BuiltIn { name, about } => {
103+
CommandInfo::BuiltIn { about } => {
104104
let summary = about.unwrap_or_default();
105105
let summary = summary.lines().next().unwrap_or(&summary); // display only the first line
106106
drop_println!(config, " {:<20} {}", name, summary);
107107
}
108-
CommandInfo::External { name, path } => {
108+
CommandInfo::External { path } => {
109109
if is_verbose {
110110
drop_println!(config, " {:<20} {}", name, path.display());
111111
} else {
112112
drop_println!(config, " {}", name);
113113
}
114114
}
115-
CommandInfo::Alias { name, target } => {
115+
CommandInfo::Alias { target } => {
116116
drop_println!(config, " {:<20} {}", name, target.iter().join(" "));
117117
}
118118
}

src/bin/cargo/main.rs

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use cargo::util::toml::StringOrVec;
88
use cargo::util::CliError;
99
use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config};
1010
use cargo_util::{ProcessBuilder, ProcessError};
11-
use std::collections::{BTreeMap, BTreeSet};
11+
use std::collections::BTreeMap;
1212
use std::env;
1313
use std::fs;
1414
use std::path::{Path, PathBuf};
@@ -84,10 +84,10 @@ fn aliased_command(config: &Config, command: &str) -> CargoResult<Option<Vec<Str
8484
}
8585

8686
/// List all runnable commands
87-
fn list_commands(config: &Config) -> BTreeSet<CommandInfo> {
87+
fn list_commands(config: &Config) -> BTreeMap<String, CommandInfo> {
8888
let prefix = "cargo-";
8989
let suffix = env::consts::EXE_SUFFIX;
90-
let mut commands = BTreeSet::new();
90+
let mut commands = BTreeMap::new();
9191
for dir in search_directories(config) {
9292
let entries = match fs::read_dir(dir) {
9393
Ok(entries) => entries,
@@ -104,37 +104,43 @@ fn list_commands(config: &Config) -> BTreeSet<CommandInfo> {
104104
}
105105
if is_executable(entry.path()) {
106106
let end = filename.len() - suffix.len();
107-
commands.insert(CommandInfo::External {
108-
name: filename[prefix.len()..end].to_string(),
109-
path: path.clone(),
110-
});
107+
commands.insert(
108+
filename[prefix.len()..end].to_string(),
109+
CommandInfo::External { path: path.clone() },
110+
);
111111
}
112112
}
113113
}
114114

115115
for cmd in commands::builtin() {
116-
commands.insert(CommandInfo::BuiltIn {
117-
name: cmd.get_name().to_string(),
118-
about: cmd.p.meta.about.map(|s| s.to_string()),
119-
});
116+
commands.insert(
117+
cmd.get_name().to_string(),
118+
CommandInfo::BuiltIn {
119+
about: cmd.p.meta.about.map(|s| s.to_string()),
120+
},
121+
);
120122
}
121123

122124
// Add the builtin_aliases and them descriptions to the
123-
// `commands` `BTreeSet`.
125+
// `commands` `BTreeMap`.
124126
for command in &BUILTIN_ALIASES {
125-
commands.insert(CommandInfo::BuiltIn {
126-
name: command.0.to_string(),
127-
about: Some(command.2.to_string()),
128-
});
127+
commands.insert(
128+
command.0.to_string(),
129+
CommandInfo::BuiltIn {
130+
about: Some(command.2.to_string()),
131+
},
132+
);
129133
}
130134

131135
// Add the user-defined aliases
132136
if let Ok(aliases) = config.get::<BTreeMap<String, StringOrVec>>("alias") {
133137
for (name, target) in aliases.iter() {
134-
commands.insert(CommandInfo::Alias {
135-
name: name.to_string(),
136-
target: target.clone(),
137-
});
138+
commands.insert(
139+
name.to_string(),
140+
CommandInfo::Alias {
141+
target: target.clone(),
142+
},
143+
);
138144
}
139145
}
140146

@@ -150,11 +156,8 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> Cli
150156
let command = match path {
151157
Some(command) => command,
152158
None => {
153-
let suggestions: Vec<_> = list_commands(config)
154-
.iter()
155-
.map(|c| c.name().to_string())
156-
.collect();
157-
let did_you_mean = closest_msg(cmd, suggestions.iter(), |c| c);
159+
let suggestions = list_commands(config);
160+
let did_you_mean = closest_msg(cmd, suggestions.keys(), |c| c);
158161
let err = anyhow::format_err!("no such subcommand: `{}`{}", cmd, did_you_mean);
159162
return Err(CliError::new(err, 101));
160163
}

src/cargo/util/command_prelude.rs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -718,17 +718,7 @@ pub fn values_os(args: &ArgMatches<'_>, name: &str) -> Vec<OsString> {
718718

719719
#[derive(PartialEq, Eq, PartialOrd, Ord)]
720720
pub enum CommandInfo {
721-
BuiltIn { name: String, about: Option<String> },
722-
External { name: String, path: PathBuf },
723-
Alias { name: String, target: StringOrVec },
724-
}
725-
726-
impl CommandInfo {
727-
pub fn name(&self) -> &str {
728-
match self {
729-
CommandInfo::BuiltIn { name, .. } => name,
730-
CommandInfo::External { name, .. } => name,
731-
CommandInfo::Alias { name, .. } => name,
732-
}
733-
}
721+
BuiltIn { about: Option<String> },
722+
External { path: PathBuf },
723+
Alias { target: StringOrVec },
734724
}

tests/testsuite/cargo_command.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,24 @@ fn list_custom_aliases_with_descriptions() {
6161
.run();
6262
}
6363

64+
#[cargo_test]
65+
fn list_dedupe() {
66+
let p = project()
67+
.executable(Path::new("path-test-1").join("cargo-dupe"), "")
68+
.executable(Path::new("path-test-2").join("cargo-dupe"), "")
69+
.build();
70+
71+
let mut path = path();
72+
path.push(p.root().join("path-test-1"));
73+
path.push(p.root().join("path-test-2"));
74+
let path = env::join_paths(path.iter()).unwrap();
75+
76+
p.cargo("--list")
77+
.env("PATH", &path)
78+
.with_stdout_contains_n(" dupe", 1)
79+
.run();
80+
}
81+
6482
#[cargo_test]
6583
fn list_command_looks_at_path() {
6684
let proj = project()

0 commit comments

Comments
 (0)