1// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
2// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
3// Ana Hobden (@hoverbear) <operator@hoverbear.org>
4//
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.
10//
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.
14
15use proc_macro2::{Ident, Span, TokenStream};
16use quote::{format_ident, quote, quote_spanned};
17use syn::{spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Variant};
18
19use crate::derives::args;
20use crate::derives::args::collect_args_fields;
21use crate::item::{Item, Kind, Name};
22use crate::utils::{is_simple_ty, subty_if_name};
23
24pub fn derive_subcommand(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
25 let ident: &Ident = &input.ident;
26
27 match input.data {
28 Data::Enum(ref e: &DataEnum) => {
29 let name: Name = Name::Derived(ident.clone());
30 let item: Item = Item::from_subcommand_enum(input, name)?;
31 let variants: Vec<(&Variant, Item)> = eimpl Iterator>
32 .variants
33 .iter()
34 .map(|variant: &Variant| {
35 let item: Item =
36 Item::from_subcommand_variant(variant, struct_casing:item.casing(), item.env_casing())?;
37 Ok((variant, item))
38 })
39 .collect::<Result<Vec<_>, syn::Error>>()?;
40 gen_for_enum(&item, item_name:ident, &input.generics, &variants)
41 }
42 _ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"),
43 }
44}
45
46pub fn gen_for_enum(
47 item: &Item,
48 item_name: &Ident,
49 generics: &Generics,
50 variants: &[(&Variant, Item)],
51) -> Result<TokenStream, syn::Error> {
52 if !matches!(&*item.kind(), Kind::Command(_)) {
53 abort! { item.kind().span(),
54 "`{}` cannot be used with `command`",
55 item.kind().name(),
56 }
57 }
58
59 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
60
61 let from_arg_matches = gen_from_arg_matches(variants)?;
62 let update_from_arg_matches = gen_update_from_arg_matches(variants)?;
63
64 let augmentation = gen_augment(variants, item, false)?;
65 let augmentation_update = gen_augment(variants, item, true)?;
66 let has_subcommand = gen_has_subcommand(variants)?;
67
68 Ok(quote! {
69 #[allow(
70 dead_code,
71 unreachable_code,
72 unused_variables,
73 unused_braces,
74 unused_qualifications,
75 )]
76 #[allow(
77 clippy::style,
78 clippy::complexity,
79 clippy::pedantic,
80 clippy::restriction,
81 clippy::perf,
82 clippy::deprecated,
83 clippy::nursery,
84 clippy::cargo,
85 clippy::suspicious_else_formatting,
86 clippy::almost_swapped,
87 )]
88 #[automatically_derived]
89 impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause {
90 fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
91 Self::from_arg_matches_mut(&mut __clap_arg_matches.clone())
92 }
93
94 #from_arg_matches
95
96 fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
97 self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone())
98 }
99 #update_from_arg_matches
100 }
101
102 #[allow(
103 dead_code,
104 unreachable_code,
105 unused_variables,
106 unused_braces,
107 unused_qualifications,
108 )]
109 #[allow(
110 clippy::style,
111 clippy::complexity,
112 clippy::pedantic,
113 clippy::restriction,
114 clippy::perf,
115 clippy::deprecated,
116 clippy::nursery,
117 clippy::cargo,
118 clippy::suspicious_else_formatting,
119 clippy::almost_swapped,
120 )]
121 #[automatically_derived]
122 impl #impl_generics clap::Subcommand for #item_name #ty_generics #where_clause {
123 fn augment_subcommands <'b>(__clap_app: clap::Command) -> clap::Command {
124 #augmentation
125 }
126 fn augment_subcommands_for_update <'b>(__clap_app: clap::Command) -> clap::Command {
127 #augmentation_update
128 }
129 fn has_subcommand(__clap_name: &str) -> bool {
130 #has_subcommand
131 }
132 }
133 })
134}
135
136fn gen_augment(
137 variants: &[(&Variant, Item)],
138 parent_item: &Item,
139 override_required: bool,
140) -> Result<TokenStream, syn::Error> {
141 use syn::Fields::*;
142
143 let app_var = Ident::new("__clap_app", Span::call_site());
144
145 let mut subcommands = Vec::new();
146 for (variant, item) in variants {
147 let kind = item.kind();
148
149 let genned = match &*kind {
150 Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None,
151
152 Kind::ExternalSubcommand => {
153 let ty = match variant.fields {
154 Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
155
156 _ => abort!(
157 variant,
158 "The enum variant marked with `external_subcommand` must be \
159 a single-typed tuple, and the type must be either `Vec<String>` \
160 or `Vec<OsString>`."
161 ),
162 };
163 let deprecations = if !override_required {
164 item.deprecations()
165 } else {
166 quote!()
167 };
168 let subty = subty_if_name(ty, "Vec").ok_or_else(|| {
169 format_err!(
170 ty.span(),
171 "The type must be `Vec<_>` \
172 to be used with `external_subcommand`."
173 )
174 })?;
175 let subcommand = quote_spanned! { kind.span()=>
176 #deprecations
177 let #app_var = #app_var
178 .external_subcommand_value_parser(clap::value_parser!(#subty));
179 };
180 Some(subcommand)
181 }
182
183 Kind::Flatten(_) => match variant.fields {
184 Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
185 let ty = &unnamed[0].ty;
186 let deprecations = if !override_required {
187 item.deprecations()
188 } else {
189 quote!()
190 };
191 let next_help_heading = item.next_help_heading();
192 let next_display_order = item.next_display_order();
193 let subcommand = if override_required {
194 quote! {
195 #deprecations
196 let #app_var = #app_var
197 #next_help_heading
198 #next_display_order;
199 let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var);
200 }
201 } else {
202 quote! {
203 #deprecations
204 let #app_var = #app_var
205 #next_help_heading
206 #next_display_order;
207 let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var);
208 }
209 };
210 Some(subcommand)
211 }
212 _ => abort!(
213 variant,
214 "`flatten` is usable only with single-typed tuple variants"
215 ),
216 },
217
218 Kind::Subcommand(_) => {
219 let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
220 let arg_block = match variant.fields {
221 Named(_) => {
222 abort!(variant, "non single-typed tuple enums are not supported")
223 }
224 Unit => quote!( #subcommand_var ),
225 Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
226 let ty = &unnamed[0].ty;
227 if override_required {
228 quote_spanned! { ty.span()=>
229 {
230 <#ty as clap::Subcommand>::augment_subcommands_for_update(#subcommand_var)
231 }
232 }
233 } else {
234 quote_spanned! { ty.span()=>
235 {
236 <#ty as clap::Subcommand>::augment_subcommands(#subcommand_var)
237 }
238 }
239 }
240 }
241 Unnamed(..) => {
242 abort!(variant, "non single-typed tuple enums are not supported")
243 }
244 };
245
246 let name = item.cased_name();
247 let deprecations = if !override_required {
248 item.deprecations()
249 } else {
250 quote!()
251 };
252 let initial_app_methods = item.initial_top_level_methods();
253 let final_from_attrs = item.final_top_level_methods();
254 let override_methods = if override_required {
255 quote_spanned! { kind.span()=>
256 .subcommand_required(false)
257 .arg_required_else_help(false)
258 }
259 } else {
260 quote!()
261 };
262 let subcommand = quote! {
263 let #app_var = #app_var.subcommand({
264 #deprecations;
265 let #subcommand_var = clap::Command::new(#name);
266 let #subcommand_var = #subcommand_var
267 .subcommand_required(true)
268 .arg_required_else_help(true);
269 let #subcommand_var = #subcommand_var #initial_app_methods;
270 let #subcommand_var = #arg_block;
271 #subcommand_var #final_from_attrs #override_methods
272 });
273 };
274 Some(subcommand)
275 }
276
277 Kind::Command(_) => {
278 let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
279 let sub_augment = match variant.fields {
280 Named(ref fields) => {
281 // Defer to `gen_augment` for adding cmd methods
282 let fields = collect_args_fields(item, fields)?;
283 args::gen_augment(&fields, &subcommand_var, item, override_required)?
284 }
285 Unit => {
286 let arg_block = quote!( #subcommand_var );
287 let initial_app_methods = item.initial_top_level_methods();
288 let final_from_attrs = item.final_top_level_methods();
289 quote! {
290 let #subcommand_var = #subcommand_var #initial_app_methods;
291 let #subcommand_var = #arg_block;
292 #subcommand_var #final_from_attrs
293 }
294 }
295 Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
296 let ty = &unnamed[0].ty;
297 let arg_block = if override_required {
298 quote_spanned! { ty.span()=>
299 {
300 <#ty as clap::Args>::augment_args_for_update(#subcommand_var)
301 }
302 }
303 } else {
304 quote_spanned! { ty.span()=>
305 {
306 <#ty as clap::Args>::augment_args(#subcommand_var)
307 }
308 }
309 };
310 let initial_app_methods = item.initial_top_level_methods();
311 let final_from_attrs = item.final_top_level_methods();
312 quote! {
313 let #subcommand_var = #subcommand_var #initial_app_methods;
314 let #subcommand_var = #arg_block;
315 #subcommand_var #final_from_attrs
316 }
317 }
318 Unnamed(..) => {
319 abort!(variant, "non single-typed tuple enums are not supported")
320 }
321 };
322
323 let deprecations = if !override_required {
324 item.deprecations()
325 } else {
326 quote!()
327 };
328 let name = item.cased_name();
329 let subcommand = quote! {
330 let #app_var = #app_var.subcommand({
331 #deprecations
332 let #subcommand_var = clap::Command::new(#name);
333 #sub_augment
334 });
335 };
336 Some(subcommand)
337 }
338 };
339 subcommands.push(genned);
340 }
341
342 let deprecations = if !override_required {
343 parent_item.deprecations()
344 } else {
345 quote!()
346 };
347 let initial_app_methods = parent_item.initial_top_level_methods();
348 let final_app_methods = parent_item.final_top_level_methods();
349 Ok(quote! {
350 #deprecations;
351 let #app_var = #app_var #initial_app_methods;
352 #( #subcommands )*;
353 #app_var #final_app_methods
354 })
355}
356
357fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
358 use syn::Fields::*;
359
360 let mut ext_subcmd = false;
361
362 let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
363 .iter()
364 .filter_map(|(variant, item)| {
365 let kind = item.kind();
366 match &*kind {
367 Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None,
368
369 Kind::ExternalSubcommand => {
370 ext_subcmd = true;
371 None
372 }
373 Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)),
374 }
375 })
376 .partition(|(_, item)| {
377 let kind = item.kind();
378 matches!(&*kind, Kind::Flatten(_))
379 });
380
381 let subcommands = variants.iter().map(|(_variant, item)| {
382 let sub_name = item.cased_name();
383 quote! {
384 if #sub_name == __clap_name {
385 return true
386 }
387 }
388 });
389 let child_subcommands = flatten_variants
390 .iter()
391 .map(|(variant, _attrs)| match variant.fields {
392 Unnamed(ref fields) if fields.unnamed.len() == 1 => {
393 let ty = &fields.unnamed[0].ty;
394 Ok(quote! {
395 if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
396 return true;
397 }
398 })
399 }
400 _ => abort!(
401 variant,
402 "`flatten` is usable only with single-typed tuple variants"
403 ),
404 })
405 .collect::<Result<Vec<_>, syn::Error>>()?;
406
407 let genned = if ext_subcmd {
408 quote! { true }
409 } else {
410 quote! {
411 #( #subcommands )*
412
413 #( #child_subcommands )else*
414
415 false
416 }
417 };
418 Ok(genned)
419}
420
421fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
422 use syn::Fields::*;
423
424 let subcommand_name_var = format_ident!("__clap_name");
425 let sub_arg_matches_var = format_ident!("__clap_arg_matches");
426
427 let mut ext_subcmd = None;
428 let mut flatten_variants = Vec::new();
429 let mut unflatten_variants = Vec::new();
430 for (variant, item) in variants {
431 let kind = item.kind();
432 match &*kind {
433 Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => {}
434
435 Kind::ExternalSubcommand => {
436 if ext_subcmd.is_some() {
437 abort!(
438 item.kind().span(),
439 "Only one variant can be marked with `external_subcommand`, \
440 this is the second"
441 );
442 }
443
444 let ty = match variant.fields {
445 Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
446
447 _ => abort!(
448 variant,
449 "The enum variant marked with `external_subcommand` must be \
450 a single-typed tuple, and the type must be either `Vec<String>` \
451 or `Vec<OsString>`."
452 ),
453 };
454
455 let (span, str_ty) = match subty_if_name(ty, "Vec") {
456 Some(subty) => {
457 if is_simple_ty(subty, "String") {
458 (subty.span(), quote!(::std::string::String))
459 } else if is_simple_ty(subty, "OsString") {
460 (subty.span(), quote!(::std::ffi::OsString))
461 } else {
462 abort!(
463 ty.span(),
464 "The type must be either `Vec<String>` or `Vec<OsString>` \
465 to be used with `external_subcommand`."
466 );
467 }
468 }
469
470 None => abort!(
471 ty.span(),
472 "The type must be either `Vec<String>` or `Vec<OsString>` \
473 to be used with `external_subcommand`."
474 ),
475 };
476
477 ext_subcmd = Some((span, &variant.ident, str_ty));
478 }
479 Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => {
480 if matches!(&*item.kind(), Kind::Flatten(_)) {
481 flatten_variants.push((variant, item));
482 } else {
483 unflatten_variants.push((variant, item));
484 }
485 }
486 }
487 }
488
489 let subcommands = unflatten_variants.iter().map(|(variant, item)| {
490 let sub_name = item.cased_name();
491 let variant_name = &variant.ident;
492 let constructor_block = match variant.fields {
493 Named(ref fields) => {
494 let fields = collect_args_fields(item, fields)?;
495 args::gen_constructor(&fields)?
496 },
497 Unit => quote!(),
498 Unnamed(ref fields) if fields.unnamed.len() == 1 => {
499 let ty = &fields.unnamed[0].ty;
500 quote!( ( <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)? ) )
501 }
502 Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
503 };
504
505 Ok(quote! {
506 if #subcommand_name_var == #sub_name && !#sub_arg_matches_var.contains_id("") {
507 return ::std::result::Result::Ok(Self :: #variant_name #constructor_block)
508 }
509 })
510 }).collect::<Result<Vec<_>, syn::Error>>()?;
511 let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| {
512 let variant_name = &variant.ident;
513 match variant.fields {
514 Unnamed(ref fields) if fields.unnamed.len() == 1 => {
515 let ty = &fields.unnamed[0].ty;
516 Ok(quote! {
517 if __clap_arg_matches
518 .subcommand_name()
519 .map(|__clap_name| <#ty as clap::Subcommand>::has_subcommand(__clap_name))
520 .unwrap_or_default()
521 {
522 let __clap_res = <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?;
523 return ::std::result::Result::Ok(Self :: #variant_name (__clap_res));
524 }
525 })
526 }
527 _ => abort!(
528 variant,
529 "`flatten` is usable only with single-typed tuple variants"
530 ),
531 }
532 }).collect::<Result<Vec<_>, syn::Error>>()?;
533
534 let wildcard = match ext_subcmd {
535 Some((span, var_name, str_ty)) => quote_spanned! { span=>
536 ::std::result::Result::Ok(Self::#var_name(
537 ::std::iter::once(#str_ty::from(#subcommand_name_var))
538 .chain(
539 #sub_arg_matches_var
540 .remove_many::<#str_ty>("")
541 .unwrap()
542 .map(#str_ty::from)
543 )
544 .collect::<::std::vec::Vec<_>>()
545 ))
546 },
547
548 None => quote! {
549 ::std::result::Result::Err(clap::Error::raw(clap::error::ErrorKind::InvalidSubcommand, format!("The subcommand '{}' wasn't recognized", #subcommand_name_var)))
550 },
551 };
552
553 let raw_deprecated = args::raw_deprecated();
554 Ok(quote! {
555 fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
556 #raw_deprecated
557
558 #( #child_subcommands )else*
559
560 if let Some((#subcommand_name_var, mut __clap_arg_sub_matches)) = __clap_arg_matches.remove_subcommand() {
561 let #sub_arg_matches_var = &mut __clap_arg_sub_matches;
562 #( #subcommands )*
563
564 #wildcard
565 } else {
566 ::std::result::Result::Err(clap::Error::raw(clap::error::ErrorKind::MissingSubcommand, "A subcommand is required but one was not provided."))
567 }
568 }
569 })
570}
571
572fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
573 use syn::Fields::*;
574
575 let (flatten, variants): (Vec<_>, Vec<_>) = variants
576 .iter()
577 .filter_map(|(variant, item)| {
578 let kind = item.kind();
579 match &*kind {
580 // Fallback to `from_arg_matches_mut`
581 Kind::Skip(_, _)
582 | Kind::Arg(_)
583 | Kind::FromGlobal(_)
584 | Kind::Value
585 | Kind::ExternalSubcommand => None,
586 Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)),
587 }
588 })
589 .partition(|(_, item)| {
590 let kind = item.kind();
591 matches!(&*kind, Kind::Flatten(_))
592 });
593
594 let subcommands = variants.iter().map(|(variant, item)| {
595 let sub_name = item.cased_name();
596 let variant_name = &variant.ident;
597 let (pattern, updater) = match variant.fields {
598 Named(ref fields) => {
599 let field_names = fields.named.iter().map(|field| {
600 field.ident.as_ref().unwrap()
601 }).collect::<Vec<_>>();
602 let fields = collect_args_fields(item, fields)?;
603 let update = args::gen_updater(&fields, false)?;
604 (quote!( { #( #field_names, )* }), quote!( { #update } ))
605 }
606 Unit => (quote!(), quote!({})),
607 Unnamed(ref fields) => {
608 if fields.unnamed.len() == 1 {
609 (
610 quote!((ref mut __clap_arg)),
611 quote!(clap::FromArgMatches::update_from_arg_matches_mut(
612 __clap_arg,
613 __clap_arg_matches
614 )?),
615 )
616 } else {
617 abort_call_site!("{}: tuple enums are not supported", variant.ident)
618 }
619 }
620 };
621
622 Ok(quote! {
623 Self :: #variant_name #pattern if #sub_name == __clap_name => {
624 let (_, mut __clap_arg_sub_matches) = __clap_arg_matches.remove_subcommand().unwrap();
625 let __clap_arg_matches = &mut __clap_arg_sub_matches;
626 #updater
627 }
628 })
629 }).collect::<Result<Vec<_>, _>>()?;
630
631 let child_subcommands = flatten.iter().map(|(variant, _attrs)| {
632 let variant_name = &variant.ident;
633 match variant.fields {
634 Unnamed(ref fields) if fields.unnamed.len() == 1 => {
635 let ty = &fields.unnamed[0].ty;
636 Ok(quote! {
637 if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
638 if let Self :: #variant_name (child) = s {
639 <#ty as clap::FromArgMatches>::update_from_arg_matches_mut(child, __clap_arg_matches)?;
640 return ::std::result::Result::Ok(());
641 }
642 }
643 })
644 }
645 _ => abort!(
646 variant,
647 "`flatten` is usable only with single-typed tuple variants"
648 ),
649 }
650 }).collect::<Result<Vec<_>, _>>()?;
651
652 let raw_deprecated = args::raw_deprecated();
653 Ok(quote! {
654 fn update_from_arg_matches_mut<'b>(
655 &mut self,
656 __clap_arg_matches: &mut clap::ArgMatches,
657 ) -> ::std::result::Result<(), clap::Error> {
658 #raw_deprecated
659
660 if let Some(__clap_name) = __clap_arg_matches.subcommand_name() {
661 match self {
662 #( #subcommands ),*
663 s => {
664 #( #child_subcommands )*
665 *s = <Self as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?;
666 }
667 }
668 }
669 ::std::result::Result::Ok(())
670 }
671 })
672}
673