From 931eac5b6a77002d6081a386b3e186b49a7171b6 Mon Sep 17 00:00:00 2001 From: Kurtis Nusbaum Date: Wed, 1 Feb 2023 04:45:13 +0000 Subject: [PATCH] feat(derive): Add "required" option for groups This adds the "required" derive option for the group creation. Needed for clap-rs#4574. --- clap_derive/src/attr.rs | 2 ++ clap_derive/src/derives/args.rs | 2 ++ clap_derive/src/item.rs | 11 +++++++++ tests/derive/groups.rs | 44 +++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/clap_derive/src/attr.rs b/clap_derive/src/attr.rs index e282a8f94edd..21fe9c680e7e 100644 --- a/clap_derive/src/attr.rs +++ b/clap_derive/src/attr.rs @@ -86,6 +86,7 @@ impl Parse for ClapAttr { let magic = match name_str.as_str() { "rename_all" => Some(MagicAttrName::RenameAll), "rename_all_env" => Some(MagicAttrName::RenameAllEnv), + "required" => Some(MagicAttrName::Required), "skip" => Some(MagicAttrName::Skip), "next_display_order" => Some(MagicAttrName::NextDisplayOrder), "next_help_heading" => Some(MagicAttrName::NextHelpHeading), @@ -168,6 +169,7 @@ pub enum MagicAttrName { Version, RenameAllEnv, RenameAll, + Required, Skip, DefaultValueT, DefaultValuesT, diff --git a/clap_derive/src/derives/args.rs b/clap_derive/src/derives/args.rs index b5d51bfca397..3b48a73e7910 100644 --- a/clap_derive/src/derives/args.rs +++ b/clap_derive/src/derives/args.rs @@ -368,6 +368,7 @@ pub fn gen_augment( quote!() } else { let group_id = parent_item.ident().unraw().to_string(); + let required = parent_item.required_group(); let literal_group_members = fields .iter() .filter_map(|(_field, item)| { @@ -404,6 +405,7 @@ pub fn gen_augment( .group( clap::ArgGroup::new(#group_id) .multiple(true) + .required(#required) .args(#literal_group_members) ) ) diff --git a/clap_derive/src/item.rs b/clap_derive/src/item.rs index 5e8272ac0c1e..40eec4333241 100644 --- a/clap_derive/src/item.rs +++ b/clap_derive/src/item.rs @@ -48,6 +48,7 @@ pub struct Item { next_help_heading: Option, is_enum: bool, is_positional: bool, + required_group: bool, skip_group: bool, kind: Sp, } @@ -272,6 +273,7 @@ impl Item { next_help_heading: None, is_enum: false, is_positional: true, + required_group: false, skip_group: false, kind, } @@ -824,6 +826,10 @@ impl Item { self.env_casing = CasingStyle::from_lit(lit); } + Some(MagicAttrName::Required) if actual_attr_kind == AttrKind::Group => { + self.required_group = true; + } + Some(MagicAttrName::Skip) if actual_attr_kind == AttrKind::Group => { self.skip_group = true; } @@ -838,6 +844,7 @@ impl Item { | Some(MagicAttrName::LongHelp) | Some(MagicAttrName::Author) | Some(MagicAttrName::Version) + | Some(MagicAttrName::Required) => { let expr = attr.value_or_abort(); self.push_method(*attr.kind.get(), attr.name.clone(), expr); @@ -1059,6 +1066,10 @@ impl Item { .any(|m| m.name != "help" && m.name != "long_help") } + pub fn required_group(&self) -> bool { + self.required_group + } + pub fn skip_group(&self) -> bool { self.skip_group } diff --git a/tests/derive/groups.rs b/tests/derive/groups.rs index 88f674a5e809..400bdb72d944 100644 --- a/tests/derive/groups.rs +++ b/tests/derive/groups.rs @@ -120,3 +120,47 @@ fn helpful_panic_on_duplicate_groups() { use clap::CommandFactory; Opt::command().debug_assert(); } + +#[test] +fn required_group() { + #[derive(Parser, Debug)] + struct Opt { + #[command(flatten)] + source: Source, + #[command(flatten)] + dest: Dest, + } + + #[derive(clap::Args, Debug)] + #[group(required)] + struct Source { + #[arg(long)] + from_path: Option, + #[arg(long)] + from_git: Option, + } + + #[derive(clap::Args, Debug)] + #[group(required = true)] + struct Dest { + #[arg(long)] + to_path: Option, + #[arg(long)] + to_git: Option, + } + + use clap::CommandFactory; + let source_id = clap::Id::from("Source"); + let dest_id = clap::Id::from("Dest"); + let opt_command = Opt::command(); + let source_group = opt_command + .get_groups() + .find(|g| g.get_id() == &source_id) + .unwrap(); + let dest_group = opt_command + .get_groups() + .find(|g| g.get_id() == &dest_id) + .unwrap(); + assert!(source_group.is_required_set()); + assert!(dest_group.is_required_set()); +}