Repository: taiki-e/cargo-hack (language: rust)

Issue:
Documentation for `--skip` is incorrect
The documentation claims that it takes a "Space-separated list of features to skip", but it actually appears to want a comma-separated list:

```console
> cargo hack --skip 'all all-implementations all-algorithms' --feature-powerset check
[...]
info: running `cargo check --no-default-features --features all,all-implementations,deflate,futures-bufread` on async-compression (31/4097)
[...]
```

```console
> cargo hack --skip 'all,all-implementations,all-algorithms' --feature-powerset check
[...]
info: running `cargo check --no-default-features --features deflate,futures-write,lzma` on async-compression (23/513)
[...]
```


Discussion / hints:

Fix patch:
diff --git a/src/cli.rs b/src/cli.rs
index b2eb09a9..6e766566 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -316,23 +316,37 @@ pub(crate) fn args(coloring: &mut Option<Coloring>) -> Result<Option<Args>> {
                     }
                 };
             }
+
             macro_rules! parse_arg2 {
                 ($ident:ident, $allow_split:expr, $pat:expr, $help:expr) => {
                     if arg == $pat {
                         let arg = args.next().ok_or_else(|| req_arg($help, subcommand.as_ref()))?;
                         if $allow_split {
-                            $ident.extend(arg.split(',').map(ToString::to_string));
+                            if arg.contains(',') {
+                                $ident.extend(arg.split(',').map(ToString::to_string));
+                            } else {
+                                $ident.extend(arg.split(' ').map(ToString::to_string));
+                            }
                         } else {
                             $ident.push(arg);
                         }
                         continue;
                     } else if arg.starts_with(concat!($pat, "=")) {
-                        let arg = arg
+                        let mut arg = arg
                             .splitn(2, '=')
                             .nth(1)
                             .ok_or_else(|| req_arg($help, subcommand.as_ref()))?;
                         if $allow_split {
-                            $ident.extend(arg.split(',').map(ToString::to_string));
+                            if arg.starts_with('\'') && arg.ends_with('\'')
+                                || arg.starts_with('"') && arg.ends_with('"')
+                            {
+                                arg = &arg[1..arg.len() - 1];
+                            }
+                            if arg.contains(',') {
+                                $ident.extend(arg.split(',').map(ToString::to_string));
+                            } else {
+                                $ident.extend(arg.split(' ').map(ToString::to_string));
+                            }
                         } else {
                             $ident.push(arg.to_string());
                         }


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index e7ae49b1..7cd33cec 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -850,3 +850,62 @@ fn skip_optional_deps() {
             "running `cargo check --no-default-features --features renemed` on optional_deps (3/3)",
         );
 }
+
+#[test]
+fn list_separator() {
+    cargo_hack()
+        .args(&["run", "--features='real,renemed'"])
+        .current_dir(test_dir("tests/fixtures/optional_deps"))
+        .output()
+        .unwrap()
+        .assert_success()
+        .assert_stderr_contains("running `cargo run --features real,renemed` on optional_deps");
+
+    cargo_hack()
+        .args(&["run", "--features=\"real,renemed\""])
+        .current_dir(test_dir("tests/fixtures/optional_deps"))
+        .output()
+        .unwrap()
+        .assert_success()
+        .assert_stderr_contains("running `cargo run --features real,renemed` on optional_deps");
+
+    cargo_hack()
+        .args(&["run", "--features=real,renemed"])
+        .current_dir(test_dir("tests/fixtures/optional_deps"))
+        .output()
+        .unwrap()
+        .assert_success()
+        .assert_stderr_contains("running `cargo run --features real,renemed` on optional_deps");
+
+    cargo_hack()
+        .args(&["run", "--features", "real,renemed"])
+        .current_dir(test_dir("tests/fixtures/optional_deps"))
+        .output()
+        .unwrap()
+        .assert_success()
+        .assert_stderr_contains("running `cargo run --features real,renemed` on optional_deps");
+
+    cargo_hack()
+        .args(&["run", "--features='real renemed'"])
+        .current_dir(test_dir("tests/fixtures/optional_deps"))
+        .output()
+        .unwrap()
+        .assert_success()
+        .assert_stderr_contains("running `cargo run --features real,renemed` on optional_deps");
+
+    cargo_hack()
+        .args(&["run", "--features=\"real renemed\""])
+        .current_dir(test_dir("tests/fixtures/optional_deps"))
+        .output()
+        .unwrap()
+        .assert_success()
+        .assert_stderr_contains("running `cargo run --features real,renemed` on optional_deps");
+
+    cargo_hack()
+        .args(&["run", "--features", "real renemed"])
+        .current_dir(test_dir("tests/fixtures/optional_deps"))
+        .output()
+        .unwrap()
+        .assert_success()
+        .assert_stderr_contains("running `cargo run --features real,renemed` on optional_deps");
+}