diff --git a/src/bin/cargo/cli.rs b/src/bin/cargo/cli.rs index 2fefa8a2d02..67fe4d8b8ef 100644 --- a/src/bin/cargo/cli.rs +++ b/src/bin/cargo/cli.rs @@ -112,6 +112,9 @@ Run with 'cargo -Z [FLAG] [SUBCOMMAND]'", drop_println!(config, " {}", name); } } + CommandInfo::Alias { name, target } => { + drop_println!(config, " {:<20} {}", name, target.iter().join(" ")); + } } } return Ok(()); diff --git a/src/bin/cargo/main.rs b/src/bin/cargo/main.rs index 6f9134b8c54..a35325eb08f 100644 --- a/src/bin/cargo/main.rs +++ b/src/bin/cargo/main.rs @@ -4,6 +4,7 @@ #![warn(clippy::redundant_clone)] use cargo::core::shell::Shell; +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}; @@ -127,15 +128,17 @@ fn list_commands(config: &Config) -> BTreeSet { }); } - commands -} - -/// List all runnable aliases -fn list_aliases(config: &Config) -> Vec { - match config.get::>("alias") { - Ok(aliases) => aliases.keys().map(|a| a.to_string()).collect(), - Err(_) => Vec::new(), + // 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 } fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> CliResult { @@ -147,13 +150,11 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> Cli let command = match path { Some(command) => command, None => { - let commands: Vec = list_commands(config) + let suggestions: Vec<_> = list_commands(config) .iter() .map(|c| c.name().to_string()) .collect(); - let aliases = list_aliases(config); - let suggestions = commands.iter().chain(aliases.iter()); - let did_you_mean = closest_msg(cmd, suggestions, |c| c); + let did_you_mean = closest_msg(cmd, suggestions.iter(), |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 61f73ffae1c..e27fc99e8cc 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -6,11 +6,12 @@ use crate::sources::CRATES_IO_REGISTRY; use crate::util::important_paths::find_root_manifest_for_wd; use crate::util::interning::InternedString; use crate::util::restricted_names::is_glob_pattern; +use crate::util::toml::{StringOrVec, TomlProfile}; +use crate::util::validate_package_name; use crate::util::{ print_available_benches, print_available_binaries, print_available_examples, print_available_packages, print_available_tests, }; -use crate::util::{toml::TomlProfile, validate_package_name}; use crate::CargoResult; use anyhow::bail; use cargo_util::paths; @@ -715,10 +716,11 @@ pub fn values_os(args: &ArgMatches<'_>, name: &str) -> Vec { args._values_of_os(name) } -#[derive(PartialEq, PartialOrd, Eq, Ord)] +#[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 { @@ -726,6 +728,7 @@ impl CommandInfo { match self { CommandInfo::BuiltIn { name, .. } => name, CommandInfo::External { name, .. } => name, + CommandInfo::Alias { name, .. } => name, } } } diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 7c472bf8b2f..6b4f5eac154 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -760,7 +760,9 @@ impl TomlProfile { } } -#[derive(Clone, Debug, Serialize, Eq, PartialEq)] +/// A StringOrVec can be parsed from either a TOML string or array, +/// but is always stored as a vector. +#[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)] pub struct StringOrVec(Vec); impl<'de> de::Deserialize<'de> for StringOrVec { @@ -797,6 +799,12 @@ impl<'de> de::Deserialize<'de> for StringOrVec { } } +impl StringOrVec { + pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, String> { + self.0.iter() + } +} + #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] #[serde(untagged, expecting = "expected a boolean or a string")] pub enum StringOrBool { diff --git a/tests/testsuite/cargo_command.rs b/tests/testsuite/cargo_command.rs index 7be5ccf25a0..1a483afc50d 100644 --- a/tests/testsuite/cargo_command.rs +++ b/tests/testsuite/cargo_command.rs @@ -10,7 +10,7 @@ use std::str; use cargo_test_support::cargo_process; use cargo_test_support::paths; use cargo_test_support::registry::Package; -use cargo_test_support::{basic_bin_manifest, basic_manifest, cargo_exe, project}; +use cargo_test_support::{basic_bin_manifest, basic_manifest, cargo_exe, project, project_in_home}; fn path() -> Vec { env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect() @@ -32,7 +32,7 @@ fn list_commands_with_descriptions() { } #[cargo_test] -fn list_aliases_with_descriptions() { +fn list_builtin_aliases_with_descriptions() { let p = project().build(); p.cargo("--list") .with_stdout_contains(" b alias: build") @@ -42,6 +42,25 @@ fn list_aliases_with_descriptions() { .run(); } +#[cargo_test] +fn list_custom_aliases_with_descriptions() { + let p = project_in_home("proj") + .file( + &paths::home().join(".cargo").join("config"), + r#" + [alias] + myaliasstr = "foo --bar" + myaliasvec = ["foo", "--bar"] + "#, + ) + .build(); + + p.cargo("--list") + .with_stdout_contains(" myaliasstr foo --bar") + .with_stdout_contains(" myaliasvec foo --bar") + .run(); +} + #[cargo_test] fn list_command_looks_at_path() { let proj = project()