diff --git a/src/bin/cargo/cli.rs b/src/bin/cargo/cli.rs index 67fe4d8b8ef..12346170111 100644 --- a/src/bin/cargo/cli.rs +++ b/src/bin/cargo/cli.rs @@ -98,21 +98,21 @@ Run with 'cargo -Z [FLAG] [SUBCOMMAND]'", if args.is_present("list") { drop_println!(config, "Installed Commands:"); - for command in list_commands(config) { + for (name, command) in list_commands(config) { match command { - CommandInfo::BuiltIn { name, about } => { + CommandInfo::BuiltIn { about } => { let summary = about.unwrap_or_default(); let summary = summary.lines().next().unwrap_or(&summary); // display only the first line drop_println!(config, " {:<20} {}", name, summary); } - CommandInfo::External { name, path } => { + CommandInfo::External { path } => { if is_verbose { drop_println!(config, " {:<20} {}", name, path.display()); } else { drop_println!(config, " {}", name); } } - CommandInfo::Alias { name, target } => { + CommandInfo::Alias { target } => { drop_println!(config, " {:<20} {}", name, target.iter().join(" ")); } } diff --git a/src/bin/cargo/main.rs b/src/bin/cargo/main.rs index a35325eb08f..58056aa8375 100644 --- a/src/bin/cargo/main.rs +++ b/src/bin/cargo/main.rs @@ -8,7 +8,7 @@ use cargo::util::toml::StringOrVec; use cargo::util::CliError; use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config}; use cargo_util::{ProcessBuilder, ProcessError}; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; use std::env; use std::fs; use std::path::{Path, PathBuf}; @@ -84,10 +84,10 @@ fn aliased_command(config: &Config, command: &str) -> CargoResult BTreeSet { +fn list_commands(config: &Config) -> BTreeMap { let prefix = "cargo-"; let suffix = env::consts::EXE_SUFFIX; - let mut commands = BTreeSet::new(); + let mut commands = BTreeMap::new(); for dir in search_directories(config) { let entries = match fs::read_dir(dir) { Ok(entries) => entries, @@ -104,37 +104,43 @@ fn list_commands(config: &Config) -> BTreeSet { } if is_executable(entry.path()) { let end = filename.len() - suffix.len(); - commands.insert(CommandInfo::External { - name: filename[prefix.len()..end].to_string(), - path: path.clone(), - }); + commands.insert( + filename[prefix.len()..end].to_string(), + CommandInfo::External { path: path.clone() }, + ); } } } for cmd in commands::builtin() { - commands.insert(CommandInfo::BuiltIn { - name: cmd.get_name().to_string(), - about: cmd.p.meta.about.map(|s| s.to_string()), - }); + commands.insert( + cmd.get_name().to_string(), + CommandInfo::BuiltIn { + about: cmd.p.meta.about.map(|s| s.to_string()), + }, + ); } // Add the builtin_aliases and them descriptions to the - // `commands` `BTreeSet`. + // `commands` `BTreeMap`. for command in &BUILTIN_ALIASES { - commands.insert(CommandInfo::BuiltIn { - name: command.0.to_string(), - about: Some(command.2.to_string()), - }); + commands.insert( + command.0.to_string(), + CommandInfo::BuiltIn { + about: Some(command.2.to_string()), + }, + ); } // Add the user-defined aliases if let Ok(aliases) = config.get::>("alias") { for (name, target) in aliases.iter() { - commands.insert(CommandInfo::Alias { - name: name.to_string(), - target: target.clone(), - }); + commands.insert( + name.to_string(), + CommandInfo::Alias { + target: target.clone(), + }, + ); } } @@ -150,11 +156,8 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> Cli let command = match path { Some(command) => command, None => { - let suggestions: Vec<_> = list_commands(config) - .iter() - .map(|c| c.name().to_string()) - .collect(); - let did_you_mean = closest_msg(cmd, suggestions.iter(), |c| c); + let suggestions = list_commands(config); + let did_you_mean = closest_msg(cmd, suggestions.keys(), |c| c); let err = anyhow::format_err!("no such subcommand: `{}`{}", cmd, did_you_mean); return Err(CliError::new(err, 101)); } diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index e27fc99e8cc..9e893928850 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -718,17 +718,7 @@ pub fn values_os(args: &ArgMatches<'_>, name: &str) -> Vec { #[derive(PartialEq, Eq, PartialOrd, Ord)] pub enum CommandInfo { - BuiltIn { name: String, about: Option }, - External { name: String, path: PathBuf }, - Alias { name: String, target: StringOrVec }, -} - -impl CommandInfo { - pub fn name(&self) -> &str { - match self { - CommandInfo::BuiltIn { name, .. } => name, - CommandInfo::External { name, .. } => name, - CommandInfo::Alias { name, .. } => name, - } - } + BuiltIn { about: Option }, + External { path: PathBuf }, + Alias { target: StringOrVec }, } diff --git a/tests/testsuite/cargo_command.rs b/tests/testsuite/cargo_command.rs index 1a483afc50d..574a6c94e78 100644 --- a/tests/testsuite/cargo_command.rs +++ b/tests/testsuite/cargo_command.rs @@ -61,6 +61,24 @@ fn list_custom_aliases_with_descriptions() { .run(); } +#[cargo_test] +fn list_dedupe() { + let p = project() + .executable(Path::new("path-test-1").join("cargo-dupe"), "") + .executable(Path::new("path-test-2").join("cargo-dupe"), "") + .build(); + + let mut path = path(); + path.push(p.root().join("path-test-1")); + path.push(p.root().join("path-test-2")); + let path = env::join_paths(path.iter()).unwrap(); + + p.cargo("--list") + .env("PATH", &path) + .with_stdout_contains_n(" dupe", 1) + .run(); +} + #[cargo_test] fn list_command_looks_at_path() { let proj = project()