]> git.proxmox.com Git - rustc.git/blob - vendor/clap_derive/src/derives/subcommand.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / vendor / clap_derive / src / derives / subcommand.rs
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 use crate::{
15 attrs::{Attrs, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
16 derives::args,
17 dummies,
18 utils::{is_simple_ty, subty_if_name, Sp},
19 };
20
21 use proc_macro2::{Ident, Span, TokenStream};
22 use proc_macro_error::{abort, abort_call_site};
23 use quote::{format_ident, quote, quote_spanned};
24 use syn::{
25 punctuated::Punctuated, spanned::Spanned, Attribute, Data, DataEnum, DeriveInput,
26 FieldsUnnamed, Generics, Token, Variant,
27 };
28
29 pub fn derive_subcommand(input: &DeriveInput) -> TokenStream {
30 let ident = &input.ident;
31
32 dummies::subcommand(ident);
33
34 match input.data {
35 Data::Enum(ref e) => gen_for_enum(ident, &input.generics, &input.attrs, e),
36 _ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"),
37 }
38 }
39
40 pub fn gen_for_enum(
41 enum_name: &Ident,
42 generics: &Generics,
43 attrs: &[Attribute],
44 e: &DataEnum,
45 ) -> TokenStream {
46 let from_arg_matches = gen_from_arg_matches_for_enum(enum_name, generics, attrs, e);
47
48 let attrs = Attrs::from_struct(
49 Span::call_site(),
50 attrs,
51 Name::Derived(enum_name.clone()),
52 Sp::call_site(DEFAULT_CASING),
53 Sp::call_site(DEFAULT_ENV_CASING),
54 );
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);
58
59 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
60
61 quote! {
62 #from_arg_matches
63
64 #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
65 #[allow(
66 clippy::style,
67 clippy::complexity,
68 clippy::pedantic,
69 clippy::restriction,
70 clippy::perf,
71 clippy::deprecated,
72 clippy::nursery,
73 clippy::cargo,
74 clippy::suspicious_else_formatting,
75 )]
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> {
79 #augmentation
80 }
81 fn augment_subcommands_for_update <'b>(__clap_app: clap::Command<'b>) -> clap::Command<'b> {
82 #augmentation_update
83 }
84 fn has_subcommand(__clap_name: &str) -> bool {
85 #has_subcommand
86 }
87 }
88 }
89 }
90
91 fn gen_from_arg_matches_for_enum(
92 name: &Ident,
93 generics: &Generics,
94 attrs: &[Attribute],
95 e: &DataEnum,
96 ) -> TokenStream {
97 let attrs = Attrs::from_struct(
98 Span::call_site(),
99 attrs,
100 Name::Derived(name.clone()),
101 Sp::call_site(DEFAULT_CASING),
102 Sp::call_site(DEFAULT_ENV_CASING),
103 );
104
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);
107
108 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
109
110 quote! {
111 #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
112 #[allow(
113 clippy::style,
114 clippy::complexity,
115 clippy::pedantic,
116 clippy::restriction,
117 clippy::perf,
118 clippy::deprecated,
119 clippy::nursery,
120 clippy::cargo,
121 clippy::suspicious_else_formatting,
122 )]
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())
127 }
128
129 #from_arg_matches
130
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())
133 }
134 #update_from_arg_matches
135 }
136 }
137 }
138
139 fn gen_augment(
140 variants: &Punctuated<Variant, Token![,]>,
141 parent_attribute: &Attrs,
142 override_required: bool,
143 ) -> TokenStream {
144 use syn::Fields::*;
145
146 let app_var = Ident::new("__clap_app", Span::call_site());
147
148 let subcommands: Vec<_> = variants
149 .iter()
150 .filter_map(|variant| {
151 let attrs = Attrs::from_variant(
152 variant,
153 parent_attribute.casing(),
154 parent_attribute.env_casing(),
155 );
156 let kind = attrs.kind();
157
158 match &*kind {
159 Kind::Skip(_) => None,
160
161 Kind::ExternalSubcommand => {
162 let ty = match variant.fields {
163 Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
164
165 _ => abort!(
166 variant,
167 "The enum variant marked with `external_subcommand` must be \
168 a single-typed tuple, and the type must be either `Vec<String>` \
169 or `Vec<OsString>`."
170 ),
171 };
172 let subcommand = match subty_if_name(ty, "Vec") {
173 Some(subty) => {
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);
177 }
178 } else {
179 quote_spanned! { kind.span()=>
180 let #app_var = #app_var.allow_external_subcommands(true);
181 }
182 }
183 }
184
185 None => abort!(
186 ty.span(),
187 "The type must be `Vec<_>` \
188 to be used with `external_subcommand`."
189 ),
190 };
191 Some(subcommand)
192 }
193
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 {
201 quote! {
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);
206 }
207 } else {
208 quote! {
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);
213 }
214 };
215 Some(subcommand)
216 }
217 _ => abort!(
218 variant,
219 "`flatten` is usable only with single-typed tuple variants"
220 ),
221 },
222
223 Kind::Subcommand(_) => {
224 let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
225 let arg_block = match variant.fields {
226 Named(_) => {
227 abort!(variant, "non single-typed tuple enums are not supported")
228 }
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()=>
234 {
235 <#ty as clap::Subcommand>::augment_subcommands_for_update(#subcommand_var)
236 }
237 }
238 } else {
239 quote_spanned! { ty.span()=>
240 {
241 <#ty as clap::Subcommand>::augment_subcommands(#subcommand_var)
242 }
243 }
244 }
245 }
246 Unnamed(..) => {
247 abort!(variant, "non single-typed tuple enums are not supported")
248 }
249 };
250
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;
259 #[allow(deprecated)]
260 let #subcommand_var = #subcommand_var.setting(clap::AppSettings::SubcommandRequiredElseHelp);
261 #subcommand_var #final_from_attrs
262 });
263 };
264 Some(subcommand)
265 }
266
267 _ => {
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)
273 }
274 Unit => {
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();
278 quote! {
279 let #subcommand_var = #subcommand_var #initial_app_methods;
280 let #subcommand_var = #arg_block;
281 #subcommand_var #final_from_attrs
282 }
283 },
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()=>
288 {
289 <#ty as clap::Args>::augment_args_for_update(#subcommand_var)
290 }
291 }
292 } else {
293 quote_spanned! { ty.span()=>
294 {
295 <#ty as clap::Args>::augment_args(#subcommand_var)
296 }
297 }
298 };
299 let initial_app_methods = attrs.initial_top_level_methods();
300 let final_from_attrs = attrs.final_top_level_methods();
301 quote! {
302 let #subcommand_var = #subcommand_var #initial_app_methods;
303 let #subcommand_var = #arg_block;
304 #subcommand_var #final_from_attrs
305 }
306 }
307 Unnamed(..) => {
308 abort!(variant, "non single-typed tuple enums are not supported")
309 }
310 };
311
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);
316 #sub_augment
317 });
318 };
319 Some(subcommand)
320 }
321 }
322 })
323 .collect();
324
325 let initial_app_methods = parent_attribute.initial_top_level_methods();
326 let final_app_methods = parent_attribute.final_top_level_methods();
327 quote! {
328 let #app_var = #app_var #initial_app_methods;
329 #( #subcommands )*;
330 #app_var #final_app_methods
331 }
332 }
333
334 fn gen_has_subcommand(
335 variants: &Punctuated<Variant, Token![,]>,
336 parent_attribute: &Attrs,
337 ) -> TokenStream {
338 use syn::Fields::*;
339
340 let mut ext_subcmd = false;
341
342 let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
343 .iter()
344 .filter_map(|variant| {
345 let attrs = Attrs::from_variant(
346 variant,
347 parent_attribute.casing(),
348 parent_attribute.env_casing(),
349 );
350
351 if let Kind::ExternalSubcommand = &*attrs.kind() {
352 ext_subcmd = true;
353 None
354 } else {
355 Some((variant, attrs))
356 }
357 })
358 .partition(|(_, attrs)| {
359 let kind = attrs.kind();
360 matches!(&*kind, Kind::Flatten)
361 });
362
363 let subcommands = variants.iter().map(|(_variant, attrs)| {
364 let sub_name = attrs.cased_name();
365 quote! {
366 if #sub_name == __clap_name {
367 return true
368 }
369 }
370 });
371 let child_subcommands = flatten_variants
372 .iter()
373 .map(|(variant, _attrs)| match variant.fields {
374 Unnamed(ref fields) if fields.unnamed.len() == 1 => {
375 let ty = &fields.unnamed[0];
376 quote! {
377 if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
378 return true;
379 }
380 }
381 }
382 _ => abort!(
383 variant,
384 "`flatten` is usable only with single-typed tuple variants"
385 ),
386 });
387
388 if ext_subcmd {
389 quote! { true }
390 } else {
391 quote! {
392 #( #subcommands )*
393
394 #( #child_subcommands )else*
395
396 false
397 }
398 }
399 }
400
401 fn gen_from_arg_matches(
402 name: &Ident,
403 variants: &Punctuated<Variant, Token![,]>,
404 parent_attribute: &Attrs,
405 ) -> TokenStream {
406 use syn::Fields::*;
407
408 let mut ext_subcmd = None;
409
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
413 .iter()
414 .filter_map(|variant| {
415 let attrs = Attrs::from_variant(
416 variant,
417 parent_attribute.casing(),
418 parent_attribute.env_casing(),
419 );
420
421 if let Kind::ExternalSubcommand = &*attrs.kind() {
422 if ext_subcmd.is_some() {
423 abort!(
424 attrs.kind().span(),
425 "Only one variant can be marked with `external_subcommand`, \
426 this is the second"
427 );
428 }
429
430 let ty = match variant.fields {
431 Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
432
433 _ => abort!(
434 variant,
435 "The enum variant marked with `external_subcommand` must be \
436 a single-typed tuple, and the type must be either `Vec<String>` \
437 or `Vec<OsString>`."
438 ),
439 };
440
441 let (span, str_ty) = match subty_if_name(ty, "Vec") {
442 Some(subty) => {
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))
447 } else {
448 abort!(
449 ty.span(),
450 "The type must be either `Vec<String>` or `Vec<OsString>` \
451 to be used with `external_subcommand`."
452 );
453 }
454 }
455
456 None => abort!(
457 ty.span(),
458 "The type must be either `Vec<String>` or `Vec<OsString>` \
459 to be used with `external_subcommand`."
460 ),
461 };
462
463 ext_subcmd = Some((span, &variant.ident, str_ty));
464 None
465 } else {
466 Some((variant, attrs))
467 }
468 })
469 .partition(|(_, attrs)| {
470 let kind = attrs.kind();
471 matches!(&*kind, Kind::Flatten)
472 });
473
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),
479 Unit => quote!(),
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)? ) )
483 }
484 Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
485 };
486
487 if cfg!(feature = "unstable-v4") {
488 quote! {
489 if #sub_name == #subcommand_name_var && !#sub_arg_matches_var.contains_id("") {
490 return ::std::result::Result::Ok(#name :: #variant_name #constructor_block)
491 }
492 }
493 } else {
494 quote! {
495 if #sub_name == #subcommand_name_var {
496 return ::std::result::Result::Ok(#name :: #variant_name #constructor_block)
497 }
498 }
499 }
500 });
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];
506 quote! {
507 if __clap_arg_matches
508 .subcommand_name()
509 .map(|__clap_name| <#ty as clap::Subcommand>::has_subcommand(__clap_name))
510 .unwrap_or_default()
511 {
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));
514 }
515 }
516 }
517 _ => abort!(
518 variant,
519 "`flatten` is usable only with single-typed tuple variants"
520 ),
521 }
522 });
523
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))
528 .chain(
529 #sub_arg_matches_var
530 .remove_many::<#str_ty>("")
531 .into_iter().flatten() // `""` isn't present, bug in `unstable-v4`
532 .map(#str_ty::from)
533 )
534 .collect::<::std::vec::Vec<_>>()
535 ))
536 },
537
538 None => quote! {
539 ::std::result::Result::Err(clap::Error::raw(clap::ErrorKind::UnrecognizedSubcommand, format!("The subcommand '{}' wasn't recognized", #subcommand_name_var)))
540 },
541 };
542
543 let raw_deprecated = args::raw_deprecated();
544 quote! {
545 fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
546 #raw_deprecated
547
548 #( #child_subcommands )else*
549
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;
552 #( #subcommands )*
553
554 #wildcard
555 } else {
556 ::std::result::Result::Err(clap::Error::raw(clap::ErrorKind::MissingSubcommand, "A subcommand is required but one was not provided."))
557 }
558 }
559 }
560 }
561
562 fn gen_update_from_arg_matches(
563 name: &Ident,
564 variants: &Punctuated<Variant, Token![,]>,
565 parent_attribute: &Attrs,
566 ) -> TokenStream {
567 use syn::Fields::*;
568
569 let (flatten, variants): (Vec<_>, Vec<_>) = variants
570 .iter()
571 .filter_map(|variant| {
572 let attrs = Attrs::from_variant(
573 variant,
574 parent_attribute.casing(),
575 parent_attribute.env_casing(),
576 );
577
578 match &*attrs.kind() {
579 // Fallback to `from_arg_matches_mut`
580 Kind::ExternalSubcommand => None,
581 _ => Some((variant, attrs)),
582 }
583 })
584 .partition(|(_, attrs)| {
585 let kind = attrs.kind();
586 matches!(&*kind, Kind::Flatten)
587 });
588
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
595 .named
596 .iter()
597 .map(|field| {
598 let attrs = Attrs::from_field(
599 field,
600 parent_attribute.casing(),
601 parent_attribute.env_casing(),
602 );
603 let field_name = field.ident.as_ref().unwrap();
604 (
605 quote!( ref mut #field_name ),
606 args::gen_updater(&fields.named, &attrs, false),
607 )
608 })
609 .unzip();
610 (quote!( { #( #fields, )* }), quote!( { #( #update )* } ))
611 }
612 Unit => (quote!(), quote!({})),
613 Unnamed(ref fields) => {
614 if fields.unnamed.len() == 1 {
615 (
616 quote!((ref mut __clap_arg)),
617 quote!(clap::FromArgMatches::update_from_arg_matches_mut(
618 __clap_arg,
619 __clap_arg_matches
620 )?),
621 )
622 } else {
623 abort_call_site!("{}: tuple enums are not supported", variant.ident)
624 }
625 }
626 };
627
628 quote! {
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;
632 #updater
633 }
634 }
635 });
636
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];
642 quote! {
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(());
647 }
648 }
649 }
650 }
651 _ => abort!(
652 variant,
653 "`flatten` is usable only with single-typed tuple variants"
654 ),
655 }
656 });
657
658 let raw_deprecated = args::raw_deprecated();
659 quote! {
660 fn update_from_arg_matches_mut<'b>(
661 &mut self,
662 __clap_arg_matches: &mut clap::ArgMatches,
663 ) -> ::std::result::Result<(), clap::Error> {
664 #raw_deprecated
665
666 if let Some(__clap_name) = __clap_arg_matches.subcommand_name() {
667 match self {
668 #( #subcommands ),*
669 s => {
670 #( #child_subcommands )*
671 *s = <Self as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?;
672 }
673 }
674 }
675 ::std::result::Result::Ok(())
676 }
677 }
678 }