Skip to content

Commit

Permalink
Added aliases to subcommand typo suggestions.
Browse files Browse the repository at this point in the history
Fixes rust-lang#7278.

Adds the ability to list all available aliases (by enumerating configuration subkeys) and to provide them to the suggestion distance calculator.

Also adds tests for alias suggestions.
  • Loading branch information
zachlute committed Aug 22, 2019
1 parent 4c3248e commit 6c06d61
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 2 deletions.
17 changes: 15 additions & 2 deletions src/bin/cargo/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ fn list_commands(config: &Config) -> BTreeSet<CommandInfo> {
commands
}

/// List all runnable aliases
fn list_aliases(config: &Config) -> Vec<String> {
match config.get_subkeys("alias") {
Ok(aliases) => aliases,
Err(_) => Vec::new(),
}
}

fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> CliResult {
let command_exe = format!("cargo-{}{}", cmd, env::consts::EXE_SUFFIX);
let path = search_directories(config)
Expand All @@ -122,8 +130,13 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> Cli
let command = match path {
Some(command) => command,
None => {
let cmds = list_commands(config);
let did_you_mean = closest_msg(cmd, cmds.iter(), |c| c.name());
let commands: Vec<String> = 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 err = failure::format_err!("no such subcommand: `{}`{}", cmd, did_you_mean);
return Err(CliError::new(err, 101));
}
Expand Down
51 changes: 51 additions & 0 deletions src/cargo/util/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,57 @@ impl Config {
Ok(Some(val.clone()))
}

// Gets the subkeys defined for a given key.
pub fn get_subkeys(&self, key: &str) -> CargoResult<Vec<String>> {
let vals = self.values()?;
let mut parts = key.split('.').enumerate();
let mut val = match vals.get(parts.next().unwrap().1) {
Some(val) => val,
None => return Ok(Vec::new()),
};

for (i, part) in parts {
match *val {
CV::Table(ref map, _) => {
val = match map.get(part) {
Some(val) => val,
None => return Ok(Vec::new()),
}
}
CV::Integer(_, ref path)
| CV::String(_, ref path)
| CV::List(_, ref path)
| CV::Boolean(_, ref path) => {
let idx = key.split('.').take(i).fold(0, |n, s| n + s.len()) + i - 1;
let key_so_far = &key[..idx];
failure::bail!(
"expected table for configuration key `{}`, \
but found {} in {}",
key_so_far,
val.desc(),
path.display()
)
}
}
}

let table = match *val {
CV::Table(ref map, _) => map,
CV::Integer(_, ref path)
| CV::String(_, ref path)
| CV::List(_, ref path)
| CV::Boolean(_, ref path) => failure::bail!(
"expected table for configuration key `{}`, \
but found {} in {}",
key,
val.desc(),
path.display()
),
};

Ok(table.keys().map(|key| key.clone()).collect())
}

// Helper primarily for testing.
pub fn set_env(&mut self, env: HashMap<String, String>) {
self.env = env;
Expand Down
43 changes: 43 additions & 0 deletions tests/testsuite/cargo_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,49 @@ error: no such subcommand: `biuld`
.run();
}

#[cargo_test]
fn find_closest_alias() {
let root = paths::root();
let my_home = root.join("my_home");
fs::create_dir(&my_home).unwrap();
File::create(&my_home.join("config"))
.unwrap()
.write_all(
br#"
[alias]
myalias = "build"
"#,
)
.unwrap();

cargo_process("myalais")
.env("CARGO_HOME", &my_home)
.with_status(101)
.with_stderr_contains(
"\
error: no such subcommand: `myalais`
<tab>Did you mean `myalias`?
",
)
.run();

// But, if no alias is defined, it must not suggest one!
cargo_process("myalais")
.with_status(101)
.with_stderr_contains(
"\
error: no such subcommand: `myalais`
",
)
.with_stderr_does_not_contain(
"\
<tab>Did you mean `myalias`?
",
)
.run();
}

// If a subcommand is more than an edit distance of 3 away, we don't make a suggestion.
#[cargo_test]
fn find_closest_dont_correct_nonsense() {
Expand Down

0 comments on commit 6c06d61

Please sign in to comment.