Support list subcommand
authorAleksey Kladov <aleksey.kladov@gmail.com>
Thu, 8 Mar 2018 20:19:27 +0000 (23:19 +0300)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Thu, 8 Mar 2018 20:31:56 +0000 (23:31 +0300)
All tests are green :tada:

src/bin/cli/mod.rs
tests/testsuite/cargo_command.rs
tests/testsuite/install.rs

index 0c410e97857fec46a34bc7c7d0833313d9fe2e15..bd15853582c6e463e5ad9f857f8e39cabc001341 100644 (file)
@@ -22,13 +22,19 @@ use cargo::ops::{self, MessageFormat, Packages, CompileOptions, CompileMode, Ver
 use cargo::sources::{GitSource, RegistrySource};
 
 use self::utils::*;
+use std::collections::BTreeSet;
+use std::env;
+use std::fs;
+use search_directories;
+use is_executable;
 
 pub fn do_main(config: &mut Config) -> CliResult {
     let args = cli().get_matches();
+    let is_verbose = args.occurrences_of("verbose") > 0;
     if args.is_present("version") {
         let version = cargo::version();
         println!("{}", version);
-        if args.occurrences_of("verbose") > 0 {
+        if is_verbose {
             println!("release: {}.{}.{}",
                      version.major,
                      version.minor,
@@ -43,6 +49,22 @@ pub fn do_main(config: &mut Config) -> CliResult {
         return Ok(());
     }
 
+    if args.is_present("list") {
+        println!("Installed Commands:");
+        for command in list_commands(config) {
+            let (command, path) = command;
+            if is_verbose {
+                match path {
+                    Some(p) => println!("    {:<20} {}", command, p),
+                    None => println!("    {:<20}", command),
+                }
+            } else {
+                println!("    {}", command);
+            }
+        }
+        return Ok(());
+    }
+
     execte_subcommand(config, args)
 }
 
@@ -595,41 +617,82 @@ Some common cargo commands are (see all commands with --list):
 
 See 'cargo help <command>' for more information on a specific command.
 ")
-        .subcommands(vec![
-            bench::cli(),
-            build::cli(),
-            check::cli(),
-            clean::cli(),
-            doc::cli(),
-            fetch::cli(),
-            generate_lockfile::cli(),
-            git_checkout::cli(),
-            init::cli(),
-            install::cli(),
-            locate_project::cli(),
-            login::cli(),
-            metadata::cli(),
-            new::cli(),
-            owner::cli(),
-            package::cli(),
-            pkgid::cli(),
-            publish::cli(),
-            read_manifest::cli(),
-            run::cli(),
-            rustc::cli(),
-            rustdoc::cli(),
-            search::cli(),
-            test::cli(),
-            uninstall::cli(),
-            update::cli(),
-            verify_project::cli(),
-            version::cli(),
-            yank::cli(),
-        ])
+        .subcommands(builtin_subcommands())
     ;
     app
 }
 
+fn builtin_subcommands() -> Vec<App> {
+    vec![
+        bench::cli(),
+        build::cli(),
+        check::cli(),
+        clean::cli(),
+        doc::cli(),
+        fetch::cli(),
+        generate_lockfile::cli(),
+        git_checkout::cli(),
+        init::cli(),
+        install::cli(),
+        locate_project::cli(),
+        login::cli(),
+        metadata::cli(),
+        new::cli(),
+        owner::cli(),
+        package::cli(),
+        pkgid::cli(),
+        publish::cli(),
+        read_manifest::cli(),
+        run::cli(),
+        rustc::cli(),
+        rustdoc::cli(),
+        search::cli(),
+        test::cli(),
+        uninstall::cli(),
+        update::cli(),
+        verify_project::cli(),
+        version::cli(),
+        yank::cli(),
+    ]
+}
+
+/// List all runnable commands
+fn list_commands(config: &Config) -> BTreeSet<(String, Option<String>)> {
+    let prefix = "cargo-";
+    let suffix = env::consts::EXE_SUFFIX;
+    let mut commands = BTreeSet::new();
+    for dir in search_directories(config) {
+        let entries = match fs::read_dir(dir) {
+            Ok(entries) => entries,
+            _ => continue,
+        };
+        for entry in entries.filter_map(|e| e.ok()) {
+            let path = entry.path();
+            let filename = match path.file_name().and_then(|s| s.to_str()) {
+                Some(filename) => filename,
+                _ => continue,
+            };
+            if !filename.starts_with(prefix) || !filename.ends_with(suffix) {
+                continue;
+            }
+            if is_executable(entry.path()) {
+                let end = filename.len() - suffix.len();
+                commands.insert(
+                    (filename[prefix.len()..end].to_string(),
+                     Some(path.display().to_string()))
+                );
+            }
+        }
+    }
+
+    for cmd in builtin_subcommands() {
+        commands.insert((cmd.get_name().to_string(), None));
+    }
+
+    commands
+}
+
+
 mod bench;
 mod build;
 mod check;
index b211d26994039b7a99b3ab43e5c3b99bdf8e13f2..598f5a717097b408e882ea0ec3bedd3aad74899c 100644 (file)
@@ -59,7 +59,6 @@ fn path() -> Vec<PathBuf> {
 }
 
 #[test]
-#[ignore]
 fn list_command_looks_at_path() {
     let proj = project("list-non-overlapping").build();
     let proj = fake_file(proj, Path::new("path-test"), "cargo-1", &FakeKind::Executable);
@@ -78,7 +77,6 @@ fn list_command_looks_at_path() {
 // windows and symlinks don't currently agree that well
 #[cfg(unix)]
 #[test]
-#[ignore]
 fn list_command_resolves_symlinks() {
     use cargotest::support::cargo_exe;
 
@@ -98,23 +96,26 @@ fn list_command_resolves_symlinks() {
 }
 
 #[test]
-#[ignore]
 fn find_closest_biuld_to_build() {
     let mut pr = cargo_process();
     pr.arg("biuld");
 
     assert_that(pr,
-                execs().with_status(101)
-                       .with_stderr("[ERROR] no such subcommand: `biuld`
+                execs().with_status(1)
+                       .with_stderr("\
+error: The subcommand 'biuld' wasn't recognized
+<tab>Did you mean 'build'?
 
-<tab>Did you mean `build`?
+If you believe you received this message in error, try re-running with 'cargo -- biuld'
 
-"));
+USAGE:
+    cargo [OPTIONS] [SUBCOMMAND]
+
+For more information try --help"));
 }
 
 // if a subcommand is more than 3 edit distance away, we don't make a suggestion
 #[test]
-#[ignore]
 fn find_closest_dont_correct_nonsense() {
     let mut pr = cargo_process();
     pr.arg("there-is-no-way-that-there-is-a-command-close-to-this")
@@ -128,7 +129,6 @@ fn find_closest_dont_correct_nonsense() {
 }
 
 #[test]
-#[ignore]
 fn displays_subcommand_on_error() {
     let mut pr = cargo_process();
     pr.arg("invalid-command");
index f95c36a052c187ed0c375cfc0984f4310145b663..f7272b1cb5f51de2cc965367b4ebf39f499c3217 100644 (file)
@@ -726,7 +726,6 @@ fn uninstall_piecemeal() {
 }
 
 #[test]
-#[ignore]
 fn subcommand_works_out_of_the_box() {
     Package::new("cargo-foo", "1.0.0")
         .file("src/main.rs", r#"
@@ -787,7 +786,6 @@ warning: be sure to add `[..]` to your PATH to be able to run the installed bina
 }
 
 #[test]
-#[ignore]
 fn reports_unsuccessful_subcommand_result() {
     Package::new("cargo-fail", "1.0.0")
         .file("src/main.rs", r#"