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