1 // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
2 // Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
3 // Ana Hobden (@hoverbear) <operator@hoverbear.org>
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 // This work was derived from Structopt (https://github.com/TeXitoi/structopt)
12 // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
13 // MIT/Apache 2.0 license.
15 attrs
::{Attrs, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING}
,
18 utils
::{is_simple_ty, subty_if_name, Sp}
,
21 use proc_macro2
::{Ident, Span, TokenStream}
;
22 use proc_macro_error
::{abort, abort_call_site}
;
23 use quote
::{format_ident, quote, quote_spanned}
;
25 punctuated
::Punctuated
, spanned
::Spanned
, Attribute
, Data
, DataEnum
, DeriveInput
,
26 FieldsUnnamed
, Generics
, Token
, Variant
,
29 pub fn derive_subcommand(input
: &DeriveInput
) -> TokenStream
{
30 let ident
= &input
.ident
;
32 dummies
::subcommand(ident
);
35 Data
::Enum(ref e
) => gen_for_enum(ident
, &input
.generics
, &input
.attrs
, e
),
36 _
=> abort_call_site
!("`#[derive(Subcommand)]` only supports enums"),
46 let from_arg_matches
= gen_from_arg_matches_for_enum(enum_name
, generics
, attrs
, e
);
48 let attrs
= Attrs
::from_struct(
51 Name
::Derived(enum_name
.clone()),
52 Sp
::call_site(DEFAULT_CASING
),
53 Sp
::call_site(DEFAULT_ENV_CASING
),
55 let augmentation
= gen_augment(&e
.variants
, &attrs
, false);
56 let augmentation_update
= gen_augment(&e
.variants
, &attrs
, true);
57 let has_subcommand
= gen_has_subcommand(&e
.variants
, &attrs
);
59 let (impl_generics
, ty_generics
, where_clause
) = generics
.split_for_impl();
64 #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
74 clippy
::suspicious_else_formatting
,
76 #[deny(clippy::correctness)]
77 impl #impl_generics clap::Subcommand for #enum_name #ty_generics #where_clause {
78 fn augment_subcommands
<'b
>(__clap_app
: clap
::Command
<'b
>) -> clap
::Command
<'b
> {
81 fn augment_subcommands_for_update
<'b
>(__clap_app
: clap
::Command
<'b
>) -> clap
::Command
<'b
> {
84 fn has_subcommand(__clap_name
: &str) -> bool
{
91 fn gen_from_arg_matches_for_enum(
97 let attrs
= Attrs
::from_struct(
100 Name
::Derived(name
.clone()),
101 Sp
::call_site(DEFAULT_CASING
),
102 Sp
::call_site(DEFAULT_ENV_CASING
),
105 let from_arg_matches
= gen_from_arg_matches(name
, &e
.variants
, &attrs
);
106 let update_from_arg_matches
= gen_update_from_arg_matches(name
, &e
.variants
, &attrs
);
108 let (impl_generics
, ty_generics
, where_clause
) = generics
.split_for_impl();
111 #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
121 clippy
::suspicious_else_formatting
,
123 #[deny(clippy::correctness)]
124 impl #impl_generics clap::FromArgMatches for #name #ty_generics #where_clause {
125 fn from_arg_matches(__clap_arg_matches
: &clap
::ArgMatches
) -> ::std
::result
::Result
<Self, clap
::Error
> {
126 Self::from_arg_matches_mut(&mut __clap_arg_matches
.clone())
131 fn update_from_arg_matches(&mut self, __clap_arg_matches
: &clap
::ArgMatches
) -> ::std
::result
::Result
<(), clap
::Error
> {
132 self.update_from_arg_matches_mut(&mut __clap_arg_matches
.clone())
134 #update_from_arg_matches
140 variants
: &Punctuated
<Variant
, Token
![,]>,
141 parent_attribute
: &Attrs
,
142 override_required
: bool
,
146 let app_var
= Ident
::new("__clap_app", Span
::call_site());
148 let subcommands
: Vec
<_
> = variants
150 .filter_map(|variant
| {
151 let attrs
= Attrs
::from_variant(
153 parent_attribute
.casing(),
154 parent_attribute
.env_casing(),
156 let kind
= attrs
.kind();
159 Kind
::Skip(_
) => None
,
161 Kind
::ExternalSubcommand
=> {
162 let ty
= match variant
.fields
{
163 Unnamed(ref fields
) if fields
.unnamed
.len() == 1 => &fields
.unnamed
[0].ty
,
167 "The enum variant marked with `external_subcommand` must be \
168 a single-typed tuple, and the type must be either `Vec<String>` \
172 let subcommand
= match subty_if_name(ty
, "Vec") {
174 if is_simple_ty(subty
, "OsString") {
175 quote_spanned
! { kind
.span()=>
176 let #app_var = #app_var.allow_external_subcommands(true).allow_invalid_utf8_for_external_subcommands(true);
179 quote_spanned
! { kind
.span()=>
180 let #app_var = #app_var.allow_external_subcommands(true);
187 "The type must be `Vec<_>` \
188 to be used with `external_subcommand`."
194 Kind
::Flatten
=> match variant
.fields
{
195 Unnamed(FieldsUnnamed { ref unnamed, .. }
) if unnamed
.len() == 1 => {
196 let ty
= &unnamed
[0];
197 let old_heading_var
= format_ident
!("__clap_old_heading");
198 let next_help_heading
= attrs
.next_help_heading();
199 let next_display_order
= attrs
.next_display_order();
200 let subcommand
= if override_required
{
202 let #old_heading_var = #app_var.get_next_help_heading();
203 let #app_var = #app_var #next_help_heading #next_display_order;
204 let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var);
205 let #app_var = #app_var.next_help_heading(#old_heading_var);
209 let #old_heading_var = #app_var.get_next_help_heading();
210 let #app_var = #app_var #next_help_heading #next_display_order;
211 let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var);
212 let #app_var = #app_var.next_help_heading(#old_heading_var);
219 "`flatten` is usable only with single-typed tuple variants"
223 Kind
::Subcommand(_
) => {
224 let subcommand_var
= Ident
::new("__clap_subcommand", Span
::call_site());
225 let arg_block
= match variant
.fields
{
227 abort
!(variant
, "non single-typed tuple enums are not supported")
229 Unit
=> quote
!( #subcommand_var ),
230 Unnamed(FieldsUnnamed { ref unnamed, .. }
) if unnamed
.len() == 1 => {
231 let ty
= &unnamed
[0];
232 if override_required
{
233 quote_spanned
! { ty
.span()=>
235 <#ty as clap::Subcommand>::augment_subcommands_for_update(#subcommand_var)
239 quote_spanned
! { ty
.span()=>
241 <#ty as clap::Subcommand>::augment_subcommands(#subcommand_var)
247 abort
!(variant
, "non single-typed tuple enums are not supported")
251 let name
= attrs
.cased_name();
252 let initial_app_methods
= attrs
.initial_top_level_methods();
253 let final_from_attrs
= attrs
.final_top_level_methods();
254 let subcommand
= quote
! {
255 let #app_var = #app_var.subcommand({
256 let #subcommand_var = clap::Command::new(#name);
257 let #subcommand_var = #subcommand_var #initial_app_methods;
258 let #subcommand_var = #arg_block;
260 let #subcommand_var = #subcommand_var.setting(clap::AppSettings::SubcommandRequiredElseHelp);
261 #subcommand_var #final_from_attrs
268 let subcommand_var
= Ident
::new("__clap_subcommand", Span
::call_site());
269 let sub_augment
= match variant
.fields
{
270 Named(ref fields
) => {
271 // Defer to `gen_augment` for adding cmd methods
272 args
::gen_augment(&fields
.named
, &subcommand_var
, &attrs
, override_required
)
275 let arg_block
= quote
!( #subcommand_var );
276 let initial_app_methods
= attrs
.initial_top_level_methods();
277 let final_from_attrs
= attrs
.final_top_level_methods();
279 let #subcommand_var = #subcommand_var #initial_app_methods;
280 let #subcommand_var = #arg_block;
281 #subcommand_var #final_from_attrs
284 Unnamed(FieldsUnnamed { ref unnamed, .. }
) if unnamed
.len() == 1 => {
285 let ty
= &unnamed
[0];
286 let arg_block
= if override_required
{
287 quote_spanned
! { ty
.span()=>
289 <#ty as clap::Args>::augment_args_for_update(#subcommand_var)
293 quote_spanned
! { ty
.span()=>
295 <#ty as clap::Args>::augment_args(#subcommand_var)
299 let initial_app_methods
= attrs
.initial_top_level_methods();
300 let final_from_attrs
= attrs
.final_top_level_methods();
302 let #subcommand_var = #subcommand_var #initial_app_methods;
303 let #subcommand_var = #arg_block;
304 #subcommand_var #final_from_attrs
308 abort
!(variant
, "non single-typed tuple enums are not supported")
312 let name
= attrs
.cased_name();
313 let subcommand
= quote
! {
314 let #app_var = #app_var.subcommand({
315 let #subcommand_var = clap::Command::new(#name);
325 let initial_app_methods
= parent_attribute
.initial_top_level_methods();
326 let final_app_methods
= parent_attribute
.final_top_level_methods();
328 let #app_var = #app_var #initial_app_methods;
330 #app_var #final_app_methods
334 fn gen_has_subcommand(
335 variants
: &Punctuated
<Variant
, Token
![,]>,
336 parent_attribute
: &Attrs
,
340 let mut ext_subcmd
= false;
342 let (flatten_variants
, variants
): (Vec
<_
>, Vec
<_
>) = variants
344 .filter_map(|variant
| {
345 let attrs
= Attrs
::from_variant(
347 parent_attribute
.casing(),
348 parent_attribute
.env_casing(),
351 if let Kind
::ExternalSubcommand
= &*attrs
.kind() {
355 Some((variant
, attrs
))
358 .partition(|(_
, attrs
)| {
359 let kind
= attrs
.kind();
360 matches
!(&*kind
, Kind
::Flatten
)
363 let subcommands
= variants
.iter().map(|(_variant
, attrs
)| {
364 let sub_name
= attrs
.cased_name();
366 if #sub_name == __clap_name {
371 let child_subcommands
= flatten_variants
373 .map(|(variant
, _attrs
)| match variant
.fields
{
374 Unnamed(ref fields
) if fields
.unnamed
.len() == 1 => {
375 let ty
= &fields
.unnamed
[0];
377 if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
384 "`flatten` is usable only with single-typed tuple variants"
394 #( #child_subcommands )else*
401 fn gen_from_arg_matches(
403 variants
: &Punctuated
<Variant
, Token
![,]>,
404 parent_attribute
: &Attrs
,
408 let mut ext_subcmd
= None
;
410 let subcommand_name_var
= format_ident
!("__clap_name");
411 let sub_arg_matches_var
= format_ident
!("__clap_arg_matches");
412 let (flatten_variants
, variants
): (Vec
<_
>, Vec
<_
>) = variants
414 .filter_map(|variant
| {
415 let attrs
= Attrs
::from_variant(
417 parent_attribute
.casing(),
418 parent_attribute
.env_casing(),
421 if let Kind
::ExternalSubcommand
= &*attrs
.kind() {
422 if ext_subcmd
.is_some() {
425 "Only one variant can be marked with `external_subcommand`, \
430 let ty
= match variant
.fields
{
431 Unnamed(ref fields
) if fields
.unnamed
.len() == 1 => &fields
.unnamed
[0].ty
,
435 "The enum variant marked with `external_subcommand` must be \
436 a single-typed tuple, and the type must be either `Vec<String>` \
441 let (span
, str_ty
) = match subty_if_name(ty
, "Vec") {
443 if is_simple_ty(subty
, "String") {
444 (subty
.span(), quote
!(::std
::string
::String
))
445 } else if is_simple_ty(subty
, "OsString") {
446 (subty
.span(), quote
!(::std
::ffi
::OsString
))
450 "The type must be either `Vec<String>` or `Vec<OsString>` \
451 to be used with `external_subcommand`."
458 "The type must be either `Vec<String>` or `Vec<OsString>` \
459 to be used with `external_subcommand`."
463 ext_subcmd
= Some((span
, &variant
.ident
, str_ty
));
466 Some((variant
, attrs
))
469 .partition(|(_
, attrs
)| {
470 let kind
= attrs
.kind();
471 matches
!(&*kind
, Kind
::Flatten
)
474 let subcommands
= variants
.iter().map(|(variant
, attrs
)| {
475 let sub_name
= attrs
.cased_name();
476 let variant_name
= &variant
.ident
;
477 let constructor_block
= match variant
.fields
{
478 Named(ref fields
) => args
::gen_constructor(&fields
.named
, attrs
),
480 Unnamed(ref fields
) if fields
.unnamed
.len() == 1 => {
481 let ty
= &fields
.unnamed
[0];
482 quote
!( ( <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)? ) )
484 Unnamed(..) => abort_call_site
!("{}: tuple enums are not supported", variant
.ident
),
487 if cfg
!(feature
= "unstable-v4") {
489 if #sub_name == #subcommand_name_var && !#sub_arg_matches_var.contains_id("") {
490 return ::std
::result
::Result
::Ok(#name :: #variant_name #constructor_block)
495 if #sub_name == #subcommand_name_var {
496 return ::std
::result
::Result
::Ok(#name :: #variant_name #constructor_block)
501 let child_subcommands
= flatten_variants
.iter().map(|(variant
, _attrs
)| {
502 let variant_name
= &variant
.ident
;
503 match variant
.fields
{
504 Unnamed(ref fields
) if fields
.unnamed
.len() == 1 => {
505 let ty
= &fields
.unnamed
[0];
507 if __clap_arg_matches
509 .map(|__clap_name
| <#ty as clap::Subcommand>::has_subcommand(__clap_name))
512 let __clap_res
= <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?;
513 return ::std
::result
::Result
::Ok(#name :: #variant_name (__clap_res));
519 "`flatten` is usable only with single-typed tuple variants"
524 let wildcard
= match ext_subcmd
{
525 Some((span
, var_name
, str_ty
)) => quote_spanned
! { span
=>
526 ::std
::result
::Result
::Ok(#name::#var_name(
527 ::std
::iter
::once(#str_ty::from(#subcommand_name_var))
530 .remove_many
::<#str_ty>("")
531 .into_iter().flatten() // `""` isn't present, bug in `unstable-v4`
534 .collect
::<::std
::vec
::Vec
<_
>>()
539 ::std
::result
::Result
::Err(clap
::Error
::raw(clap
::ErrorKind
::UnrecognizedSubcommand
, format
!("The subcommand '{}' wasn't recognized", #subcommand_name_var)))
543 let raw_deprecated
= args
::raw_deprecated();
545 fn from_arg_matches_mut(__clap_arg_matches
: &mut clap
::ArgMatches
) -> ::std
::result
::Result
<Self, clap
::Error
> {
548 #( #child_subcommands )else*
550 if let Some((#subcommand_name_var, mut __clap_arg_sub_matches)) = __clap_arg_matches.remove_subcommand() {
551 let #sub_arg_matches_var = &mut __clap_arg_sub_matches;
556 ::std
::result
::Result
::Err(clap
::Error
::raw(clap
::ErrorKind
::MissingSubcommand
, "A subcommand is required but one was not provided."))
562 fn gen_update_from_arg_matches(
564 variants
: &Punctuated
<Variant
, Token
![,]>,
565 parent_attribute
: &Attrs
,
569 let (flatten
, variants
): (Vec
<_
>, Vec
<_
>) = variants
571 .filter_map(|variant
| {
572 let attrs
= Attrs
::from_variant(
574 parent_attribute
.casing(),
575 parent_attribute
.env_casing(),
578 match &*attrs
.kind() {
579 // Fallback to `from_arg_matches_mut`
580 Kind
::ExternalSubcommand
=> None
,
581 _
=> Some((variant
, attrs
)),
584 .partition(|(_
, attrs
)| {
585 let kind
= attrs
.kind();
586 matches
!(&*kind
, Kind
::Flatten
)
589 let subcommands
= variants
.iter().map(|(variant
, attrs
)| {
590 let sub_name
= attrs
.cased_name();
591 let variant_name
= &variant
.ident
;
592 let (pattern
, updater
) = match variant
.fields
{
593 Named(ref fields
) => {
594 let (fields
, update
): (Vec
<_
>, Vec
<_
>) = fields
598 let attrs
= Attrs
::from_field(
600 parent_attribute
.casing(),
601 parent_attribute
.env_casing(),
603 let field_name
= field
.ident
.as_ref().unwrap();
605 quote
!( ref mut #field_name ),
606 args
::gen_updater(&fields
.named
, &attrs
, false),
610 (quote
!( { #( #fields, )* }
), quote
!( { #( #update )* }
))
612 Unit
=> (quote
!(), quote
!({}
)),
613 Unnamed(ref fields
) => {
614 if fields
.unnamed
.len() == 1 {
616 quote
!((ref mut __clap_arg
)),
617 quote
!(clap
::FromArgMatches
::update_from_arg_matches_mut(
623 abort_call_site
!("{}: tuple enums are not supported", variant
.ident
)
629 #name :: #variant_name #pattern if #sub_name == __clap_name => {
630 let (_
, mut __clap_arg_sub_matches
) = __clap_arg_matches
.remove_subcommand().unwrap();
631 let __clap_arg_matches
= &mut __clap_arg_sub_matches
;
637 let child_subcommands
= flatten
.iter().map(|(variant
, _attrs
)| {
638 let variant_name
= &variant
.ident
;
639 match variant
.fields
{
640 Unnamed(ref fields
) if fields
.unnamed
.len() == 1 => {
641 let ty
= &fields
.unnamed
[0];
643 if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
644 if let #name :: #variant_name (child) = s {
645 <#ty as clap::FromArgMatches>::update_from_arg_matches_mut(child, __clap_arg_matches)?;
646 return ::std
::result
::Result
::Ok(());
653 "`flatten` is usable only with single-typed tuple variants"
658 let raw_deprecated
= args
::raw_deprecated();
660 fn update_from_arg_matches_mut
<'b
>(
662 __clap_arg_matches
: &mut clap
::ArgMatches
,
663 ) -> ::std
::result
::Result
<(), clap
::Error
> {
666 if let Some(__clap_name
) = __clap_arg_matches
.subcommand_name() {
670 #( #child_subcommands )*
671 *s
= <Self as clap
::FromArgMatches
>::from_arg_matches_mut(__clap_arg_matches
)?
;
675 ::std
::result
::Result
::Ok(())