Repository: clap-rs/clap
Pull request #1523 (2019-07-21 00:10:16)

imp(macros): add subcommand expr macro
Was using some hacks to use both the macros and subcommands with hyphens in their names (example based on a cli tool I use a lot: https://concourse-ci.org/setting-pipelines.html)
```
clap_app!(fly =>
  (subcommand: clap_app! { @app (SubCommand::with_name("get-pipeline"))
    (alias: "gp")
    ...
  })
  (subcommand: SubCommand::with_name("list-pipelines").alias("lp"))
)
```

Added some parsing rules to allow alternative parsing of `expr` subcommand names similar to that used for `(@arg ("non-ident-name"): --("non-ident-flag")`:
```
clap_app!(fly =>
  (@subcommand ("get-pipeline") =>
    (alias: "gp")
    ...
  )
  (@subcommand ("list-pipelines") => (alias: "lp"))
)
```
and
```
clap_app!(@subcommand ("set-pipeline") => (alias: "sp"))
```

kbknapp: Could you rebase onto the latest master (which is now at v3-beta). Thanks!
shua: @kbknapp dunzo, waiting to see if I have received the appveyor+travis blessing.
kbknapp: @shua Sorry for the headache...there was some confusion due to my absence over the past few months...

v3-master "just now" became master in prep for a beta release. This PR was autoclosed when the v3-master branch went away. This will need one final rebase onto master :worried: Again, apologies

Patch:
diff --git a/src/macros.rs b/src/macros.rs
index 792c7c00..5662d289 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -643,6 +643,12 @@ macro_rules! app_from_crate {
 /// * There are short hand syntaxes for `ArgGroup` methods that accept booleans
 ///   * A plus sign will set that method to `true` such as `+required` = `ArgGroup::required(true)`
 ///   * An exclamation will set that method to `false` such as `!required` = `ArgGroup::required(false)`
+/// 
+/// # Alternative form for non-ident values
+/// 
+/// Certain places that normally accept an `ident`, will optionally accept an alternative of `("expr enclosed by parens")`
+/// * `(@arg something: --something)` could also be `(@arg ("something-else"): --("something-else"))`
+/// * `(@subcommand something => ...)` could also be `(@subcommand ("something-else") => ...)`
 ///
 /// [`Arg::short`]: ./struct.Arg.html#method.short
 /// [`Arg::long`]: ./struct.Arg.html#method.long
@@ -706,6 +712,14 @@ macro_rules! clap_app {
             $($tt)*
         }
     };
+    (@app ($builder:expr) (@subcommand ($name:expr) => $($tail:tt)*) $($tt:tt)*) => {
+        clap_app!{ @app
+            ($builder.subcommand(
+                $crate::clap_app!{ @app ($crate::App::new($name)) $($tail)* }
+            ))
+            $($tt)*
+        }
+    };
 // Yaml like function calls - used for setting various meta directly against the app
     (@app ($builder:expr) ($ident:ident: $($v:expr),*) $($tt:tt)*) => {
 // clap_app!{ @app ($builder.$ident($($v),*)) $($tt)* }
@@ -791,6 +805,9 @@ macro_rules! clap_app {
     (@subcommand $name:ident => $($tail:tt)*) => {
         $crate::clap_app!{ @app ($crate::App::new(stringify!($name))) $($tail)* }
     };
+    (@subcommand ($name:expr) => $($tail:tt)*) => {
+        $crate::clap_app!{ @app ($crate::App::new($name)) $($tail)* }
+    };
 // Start the magic
     (($name:expr) => $($tail:tt)*) => {{
         $crate::clap_app!{ @app ($crate::App::new($name)) $($tail)*}


Test patch:
diff --git a/tests/macros.rs b/tests/macros.rs
index 124228c4..d4e5f4a1 100644
--- a/tests/macros.rs
+++ b/tests/macros.rs
@@ -153,6 +153,41 @@ fn quoted_arg_name() {
     assert!(matches.is_present("option2"));
 }
 
+#[test]
+fn quoted_subcommand_name() {
+    clap_app!(claptests =>
+        (version: "0.1")
+        (about: "tests clap library")
+        (author: "Kevin K. <kbknapp@gmail.com>")
+        (@arg opt: -o --option +takes_value ... "tests options")
+        (@arg positional: index(1) "tests positionals")
+        (@arg flag: -f --flag ... +global "tests flags")
+        (@arg flag2: -F conflicts_with[flag] requires[option2]
+            "tests flags with exclusions")
+        (@arg option2: --long_option_2 conflicts_with[option] requires[positional2]
+            "tests long options with exclusions")
+        (@arg positional2: index(2) "tests positionals with exclusions")
+        (@arg option3: -O --Option +takes_value possible_value[fast slow]
+            "tests options with specific value sets")
+        (@arg positional3: index(3) ... possible_value[vi emacs]
+            "tests positionals with specific values")
+        (@arg multvals: --multvals +takes_value value_name[one two]
+            "Tests multiple values, not mult occs")
+        (@arg multvalsmo: --multvalsmo ... +takes_value value_name[one two]
+            "Tests multiple values, not mult occs")
+        (@arg minvals: --minvals2 min_values(1) ... +takes_value "Tests 2 min vals")
+        (@arg maxvals: --maxvals3 ... +takes_value max_values(3) "Tests 3 max vals")
+        (@subcommand subcmd =>
+            (about: "tests subcommands")
+            (version: "0.1")
+            (author: "Kevin K. <kbknapp@gmail.com>")
+            (@arg scoption: -o --option ... +takes_value "tests options")
+            (@arg scpositional: index(1) "tests positionals"))
+        (@subcommand ("other-subcmd") =>
+            (about: "some other subcommand"))
+    );
+}
+
 #[test]
 fn group_macro() {
     let app = clap_app!(claptests =>