Add 'did you mean...' message when a subcommand is not found.

This commit is contained in:
Paul Woolcock 2014-11-05 21:05:01 -05:00
parent 537b43c2b5
commit 12f5de8e9b
2 changed files with 51 additions and 3 deletions

View file

@ -133,11 +133,31 @@ fn execute(flags: Flags, shell: &mut MultiShell) -> CliResult<Option<()>> {
Ok(None) Ok(None)
} }
fn find_closest(cmd: &str) -> Option<String> {
match list_commands().iter()
// doing it this way (instead of just .min_by(|c| c.lev_distance(cmd)))
// allows us to only make suggestions that have an edit distance of
// 3 or less
.map(|c| (c.lev_distance(cmd), c))
.filter(|&(d, _): &(uint, &String)| d < 4u)
.min_by(|&(d, _)| d) {
Some((_, c)) => {
Some(c.to_string())
},
None => None
}
}
fn execute_subcommand(cmd: &str, args: &[String], shell: &mut MultiShell) { fn execute_subcommand(cmd: &str, args: &[String], shell: &mut MultiShell) {
let command = match find_command(cmd) { let command = match find_command(cmd) {
Some(command) => command, Some(command) => command,
None => return handle_error(CliError::new("No such subcommand", 127), None => {
shell) let msg = match find_closest(cmd) {
Some(closest) => format!("No such subcommand\n\n\tDid you mean ``{}''?\n", closest),
None => "No such subcommand".to_string()
};
return handle_error(CliError::new(msg, 127), shell)
}
}; };
let status = Command::new(command) let status = Command::new(command)
.args(args) .args(args)

View file

@ -5,7 +5,8 @@ use std::str;
use cargo::util::process; use cargo::util::process;
use support::paths; use support::paths;
use support::{project, cargo_dir, mkdir_recursive, ProjectBuilder, ResultTest}; use support::{execs, project, cargo_dir, mkdir_recursive, ProjectBuilder, ResultTest};
use hamcrest::{assert_that};
fn setup() { fn setup() {
} }
@ -44,3 +45,30 @@ test!(list_commands_looks_at_path {
let output = str::from_utf8(output.output.as_slice()).assert(); let output = str::from_utf8(output.output.as_slice()).assert();
assert!(output.contains("\n 1\n"), "missing 1: {}", output); assert!(output.contains("\n 1\n"), "missing 1: {}", output);
}) })
test!(find_closest_biuld_to_build {
let pr = process(cargo_dir().join("cargo"))
.arg("biuld").cwd(paths::root())
.env("HOME", Some(paths::home()));
assert_that(pr,
execs().with_status(127)
.with_stderr("No such subcommand
Did you mean ``build''?
"));
})
// if a subcommand is more than 3 edit distance away, we don't make a suggestion
test!(find_closest_dont_correct_nonsense {
let pr = process(cargo_dir().join("cargo"))
.arg("asdf").cwd(paths::root())
.env("HOME", Some(paths::home()));
assert_that(pr,
execs().with_status(127)
.with_stderr("No such subcommand
"));
})