feat(derive): Allow skipping the implicit ArgGroup

This was prioritized to allow users to workaround problems when the
implicit `ArgGroup` is getting in the way.

Fixes #4279
This commit is contained in:
Ed Page 2022-09-30 09:13:38 -05:00
parent 35eeba0b63
commit 2498147138
4 changed files with 35 additions and 3 deletions

View file

@ -315,11 +315,19 @@ pub fn gen_augment(
let initial_app_methods = parent_item.initial_top_level_methods(); let initial_app_methods = parent_item.initial_top_level_methods();
let group_id = parent_item.ident().unraw().to_string(); let group_id = parent_item.ident().unraw().to_string();
let final_app_methods = parent_item.final_top_level_methods(); let final_app_methods = parent_item.final_top_level_methods();
let group_app_methods = if parent_item.skip_group() {
quote!()
} else {
quote!(
.group(clap::ArgGroup::new(#group_id).multiple(true))
)
};
quote! {{ quote! {{
#deprecations #deprecations
let #app_var = #app_var let #app_var = #app_var
#initial_app_methods #initial_app_methods
.group(clap::ArgGroup::new(#group_id).multiple(true)); #group_app_methods
;
#( #args )* #( #args )*
#app_var #final_app_methods #app_var #final_app_methods
}} }}

View file

@ -50,6 +50,7 @@ pub struct Item {
next_help_heading: Option<Method>, next_help_heading: Option<Method>,
is_enum: bool, is_enum: bool,
is_positional: bool, is_positional: bool,
skip_group: bool,
kind: Sp<Kind>, kind: Sp<Kind>,
} }
@ -272,6 +273,7 @@ impl Item {
next_help_heading: None, next_help_heading: None,
is_enum: false, is_enum: false,
is_positional: true, is_positional: true,
skip_group: false,
kind, kind,
} }
} }
@ -334,6 +336,7 @@ impl Item {
continue; continue;
} }
let actual_attr_kind = *attr.kind.get();
let kind = match &attr.magic { let kind = match &attr.magic {
Some(MagicAttrName::FromGlobal) => { Some(MagicAttrName::FromGlobal) => {
if attr.value.is_some() { if attr.value.is_some() {
@ -377,7 +380,7 @@ impl Item {
let kind = Sp::new(Kind::Flatten, attr.name.clone().span()); let kind = Sp::new(Kind::Flatten, attr.name.clone().span());
Some(kind) Some(kind)
} }
Some(MagicAttrName::Skip) => { Some(MagicAttrName::Skip) if actual_attr_kind != AttrKind::Group => {
let expr = attr.value.clone(); let expr = attr.value.clone();
let kind = Sp::new( let kind = Sp::new(
Kind::Skip(expr, self.kind.attr_kind()), Kind::Skip(expr, self.kind.attr_kind()),
@ -408,6 +411,8 @@ impl Item {
)); ));
} }
(AttrKind::Group, AttrKind::Command) => {}
_ if attr.kind != expected_attr_kind => { _ if attr.kind != expected_attr_kind => {
abort!( abort!(
attr.kind.span(), attr.kind.span(),
@ -803,6 +808,10 @@ impl Item {
self.env_casing = CasingStyle::from_lit(lit); self.env_casing = CasingStyle::from_lit(lit);
} }
Some(MagicAttrName::Skip) if actual_attr_kind == AttrKind::Group => {
self.skip_group = true;
}
None None
// Magic only for the default, otherwise just forward to the builder // Magic only for the default, otherwise just forward to the builder
| Some(MagicAttrName::Short) | Some(MagicAttrName::Short)
@ -1029,6 +1038,10 @@ impl Item {
.iter() .iter()
.any(|m| m.name != "help" && m.name != "long_help") .any(|m| m.name != "help" && m.name != "long_help")
} }
pub fn skip_group(&self) -> bool {
self.skip_group
}
} }
#[derive(Clone)] #[derive(Clone)]

View file

@ -4,6 +4,7 @@
//! 2. [Attributes](#attributes) //! 2. [Attributes](#attributes)
//! 1. [Terminology](#terminology) //! 1. [Terminology](#terminology)
//! 2. [Command Attributes](#command-attributes) //! 2. [Command Attributes](#command-attributes)
//! 2. [ArgGroup Attributes](#arggroup-attributes)
//! 3. [Arg Attributes](#arg-attributes) //! 3. [Arg Attributes](#arg-attributes)
//! 4. [ValueEnum Attributes](#valueenum-attributes) //! 4. [ValueEnum Attributes](#valueenum-attributes)
//! 5. [Possible Value Attributes](#possible-value-attributes) //! 5. [Possible Value Attributes](#possible-value-attributes)
@ -28,6 +29,7 @@
//! /// Doc comment //! /// Doc comment
//! #[derive(Parser)] //! #[derive(Parser)]
//! #[command(CMD ATTRIBUTE)] //! #[command(CMD ATTRIBUTE)]
//! #[group(GROUP ATTRIBUTE)]
//! struct Cli { //! struct Cli {
//! /// Doc comment //! /// Doc comment
//! #[arg(ARG ATTRIBUTE)] //! #[arg(ARG ATTRIBUTE)]
@ -46,6 +48,7 @@
//! /// Doc comment //! /// Doc comment
//! #[derive(Args)] //! #[derive(Args)]
//! #[command(PARENT CMD ATTRIBUTE)] //! #[command(PARENT CMD ATTRIBUTE)]
//! #[group(GROUP ATTRIBUTE)]
//! struct Struct { //! struct Struct {
//! /// Doc comment //! /// Doc comment
//! #[command(ARG ATTRIBUTE)] //! #[command(ARG ATTRIBUTE)]
@ -173,6 +176,13 @@
//! - `external_subcommand`: [`Command::allow_external_subcommand(true)`][crate::Command::allow_external_subcommands] //! - `external_subcommand`: [`Command::allow_external_subcommand(true)`][crate::Command::allow_external_subcommands]
//! - Variant must be either `Variant(Vec<String>)` or `Variant(Vec<OsString>)` //! - Variant must be either `Variant(Vec<String>)` or `Variant(Vec<OsString>)`
//! //!
//! ### ArgGroup Attributes
//!
//! These correspond to the [`ArgGroup`][crate::ArgGroup] which is implicitly created for each
//! `Args` derive.
//!
//! At the moment, only `#[group(skip)]` is supported
//!
//! ### Arg Attributes //! ### Arg Attributes
//! //!
//! These correspond to a [`Arg`][crate::Arg]. //! These correspond to a [`Arg`][crate::Arg].

View file

@ -23,7 +23,6 @@ fn test_safely_nest_parser() {
} }
#[test] #[test]
#[should_panic = "'Compose' is already in use"]
fn skip_group_avoids_duplicate_ids() { fn skip_group_avoids_duplicate_ids() {
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct Opt { struct Opt {
@ -34,6 +33,7 @@ fn skip_group_avoids_duplicate_ids() {
} }
#[derive(clap::Args, Debug)] #[derive(clap::Args, Debug)]
#[group(skip)]
pub struct Compose<L: clap::Args, R: clap::Args> { pub struct Compose<L: clap::Args, R: clap::Args> {
#[clap(flatten)] #[clap(flatten)]
pub left: L, pub left: L,
@ -42,6 +42,7 @@ fn skip_group_avoids_duplicate_ids() {
} }
#[derive(clap::Args, Clone, Copy, Debug)] #[derive(clap::Args, Clone, Copy, Debug)]
#[group(skip)]
pub struct Empty; pub struct Empty;
use clap::CommandFactory; use clap::CommandFactory;