diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index d0bdc10a9..d46b85a7a 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -133,11 +133,31 @@ fn execute(flags: Flags, shell: &mut MultiShell) -> CliResult> { Ok(None) } +fn find_closest(cmd: &str) -> Option { + 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) { let command = match find_command(cmd) { Some(command) => command, - None => return handle_error(CliError::new("No such subcommand", 127), - shell) + None => { + 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) .args(args) diff --git a/tests/test_cargo.rs b/tests/test_cargo.rs index 6db2f77fa..d70948ccb 100644 --- a/tests/test_cargo.rs +++ b/tests/test_cargo.rs @@ -5,7 +5,8 @@ use std::str; use cargo::util::process; 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() { } @@ -44,3 +45,30 @@ test!(list_commands_looks_at_path { let output = str::from_utf8(output.output.as_slice()).assert(); 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 +")); +}) +