1 // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8
9 //! This crate is custom derive for `StructOpt`. It should not be used
10 //! directly. See [structopt documentation](https://docs.rs/structopt)
11 //! for the usage of `#[derive(StructOpt)]`.
12
13 #![allow(clippy::large_enum_variant)]
14 // FIXME: remove when and if our MSRV hits 1.42
15 #![allow(clippy::match_like_matches_macro)]
16 #![forbid(unsafe_code)]
17
18 extern crate proc_macro;
19
20 mod attrs;
21 mod doc_comments;
22 mod parse;
23 mod spanned;
24 mod ty;
25
26 use crate::{
27 attrs::{Attrs, CasingStyle, Kind, Name, ParserKind},
28 spanned::Sp,
29 ty::{is_simple_ty, sub_type, subty_if_name, Ty},
30 };
31
32 use proc_macro2::{Span, TokenStream};
33 use proc_macro_error::{abort, abort_call_site, proc_macro_error, set_dummy};
34 use quote::{format_ident, quote, quote_spanned};
35 use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, *};
36
37 /// Default casing style for generated arguments.
38 const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
39
40 /// Default casing style for environment variables
41 const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
42
43 /// Output for the `gen_xxx()` methods were we need more than a simple stream of tokens.
44 ///
45 /// The output of a generation method is not only the stream of new tokens but also the attribute
46 /// information of the current element. These attribute information may contain valuable information
47 /// for any kind of child arguments.
48 struct GenOutput {
49 tokens: TokenStream,
50 attrs: Attrs,
51 }
52
53 /// Generates the `StructOpt` impl.
54 #[proc_macro_derive(StructOpt, attributes(structopt))]
55 #[proc_macro_error]
structopt(input: proc_macro::TokenStream) -> proc_macro::TokenStream56 pub fn structopt(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
57 let input: DeriveInput = syn::parse(input).unwrap();
58 let gen = impl_structopt(&input);
59 gen.into()
60 }
61
62 /// Generate a block of code to add arguments/subcommands corresponding to
63 /// the `fields` to an app.
gen_augmentation( fields: &Punctuated<Field, Comma>, app_var: &Ident, parent_attribute: &Attrs, ) -> TokenStream64 fn gen_augmentation(
65 fields: &Punctuated<Field, Comma>,
66 app_var: &Ident,
67 parent_attribute: &Attrs,
68 ) -> TokenStream {
69 let mut subcmds = fields.iter().filter_map(|field| {
70 let attrs = Attrs::from_field(
71 field,
72 Some(parent_attribute),
73 parent_attribute.casing(),
74 parent_attribute.env_casing(),
75 );
76 let kind = attrs.kind();
77 if let Kind::Subcommand(ty) = &*kind {
78 let subcmd_type = match (**ty, sub_type(&field.ty)) {
79 (Ty::Option, Some(sub_type)) => sub_type,
80 _ => &field.ty,
81 };
82 let required = if **ty == Ty::Option {
83 quote!()
84 } else {
85 quote_spanned! { kind.span()=>
86 let #app_var = #app_var.setting(
87 ::structopt::clap::AppSettings::SubcommandRequiredElseHelp
88 );
89 }
90 };
91
92 let span = field.span();
93 let ts = quote! {
94 let #app_var = <#subcmd_type as ::structopt::StructOptInternal>::augment_clap(
95 #app_var
96 );
97 #required
98 };
99 Some((span, ts))
100 } else {
101 None
102 }
103 });
104
105 let subcmd = subcmds.next().map(|(_, ts)| ts);
106 if let Some((span, _)) = subcmds.next() {
107 abort!(
108 span,
109 "multiple subcommand sets are not allowed, that's the second"
110 );
111 }
112
113 let args = fields.iter().filter_map(|field| {
114 let attrs = Attrs::from_field(
115 field,
116 Some(parent_attribute),
117 parent_attribute.casing(),
118 parent_attribute.env_casing(),
119 );
120 let kind = attrs.kind();
121 match &*kind {
122 Kind::ExternalSubcommand => abort!(
123 kind.span(),
124 "`external_subcommand` is only allowed on enum variants"
125 ),
126 Kind::Subcommand(_) | Kind::Skip(_) => None,
127 Kind::Flatten => {
128 let ty = &field.ty;
129 Some(quote_spanned! { kind.span()=>
130 let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap(#app_var);
131 let #app_var = if <#ty as ::structopt::StructOptInternal>::is_subcommand() {
132 #app_var.setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp)
133 } else {
134 #app_var
135 };
136 })
137 }
138 Kind::Arg(ty) => {
139 let convert_type = match **ty {
140 Ty::Vec | Ty::Option => sub_type(&field.ty).unwrap_or(&field.ty),
141 Ty::OptionOption | Ty::OptionVec => {
142 sub_type(&field.ty).and_then(sub_type).unwrap_or(&field.ty)
143 }
144 _ => &field.ty,
145 };
146
147 let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
148 let flag = *attrs.parser().kind == ParserKind::FromFlag;
149
150 let parser = attrs.parser();
151 let func = &parser.func;
152 let validator = match *parser.kind {
153 ParserKind::TryFromStr => quote_spanned! { func.span()=>
154 .validator(|s| {
155 #func(s.as_str())
156 .map(|_: #convert_type| ())
157 .map_err(|e| e.to_string())
158 })
159 },
160 ParserKind::TryFromOsStr => quote_spanned! { func.span()=>
161 .validator_os(|s| #func(&s).map(|_: #convert_type| ()))
162 },
163 _ => quote!(),
164 };
165
166 let modifier = match **ty {
167 Ty::Bool => quote_spanned! { ty.span()=>
168 .takes_value(false)
169 .multiple(false)
170 },
171
172 Ty::Option => quote_spanned! { ty.span()=>
173 .takes_value(true)
174 .multiple(false)
175 #validator
176 },
177
178 Ty::OptionOption => quote_spanned! { ty.span()=>
179 .takes_value(true)
180 .multiple(false)
181 .min_values(0)
182 .max_values(1)
183 #validator
184 },
185
186 Ty::OptionVec => quote_spanned! { ty.span()=>
187 .takes_value(true)
188 .multiple(true)
189 .min_values(0)
190 #validator
191 },
192
193 Ty::Vec => quote_spanned! { ty.span()=>
194 .takes_value(true)
195 .multiple(true)
196 #validator
197 },
198
199 Ty::Other if occurrences => quote_spanned! { ty.span()=>
200 .takes_value(false)
201 .multiple(true)
202 },
203
204 Ty::Other if flag => quote_spanned! { ty.span()=>
205 .takes_value(false)
206 .multiple(false)
207 },
208
209 Ty::Other => {
210 let required = !attrs.has_method("default_value");
211 quote_spanned! { ty.span()=>
212 .takes_value(true)
213 .multiple(false)
214 .required(#required)
215 #validator
216 }
217 }
218 };
219
220 let name = attrs.cased_name();
221 let methods = attrs.field_methods();
222
223 Some(quote_spanned! { field.span()=>
224 let #app_var = #app_var.arg(
225 ::structopt::clap::Arg::with_name(#name)
226 #modifier
227 #methods
228 );
229 })
230 }
231 }
232 });
233
234 let app_methods = parent_attribute.top_level_methods();
235 let version = parent_attribute.version();
236 quote! {{
237 let #app_var = #app_var#app_methods;
238 #( #args )*
239 #subcmd
240 #app_var#version
241 }}
242 }
243
gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream244 fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
245 // This ident is used in several match branches below,
246 // and the `quote[_spanned]` invocations have different spans.
247 //
248 // Given that this ident is used in several places and
249 // that the branches are located inside of a loop, it is possible that
250 // this ident will be given _different_ spans in different places, and
251 // thus will not be the _same_ ident anymore. To make sure the `matches`
252 // is always the same, we factor it out.
253 let matches = format_ident!("matches");
254
255 let fields = fields.iter().map(|field| {
256 let attrs = Attrs::from_field(
257 field,
258 Some(parent_attribute),
259 parent_attribute.casing(),
260 parent_attribute.env_casing(),
261 );
262 let field_name = field.ident.as_ref().unwrap();
263 let kind = attrs.kind();
264 match &*kind {
265 Kind::ExternalSubcommand => abort!(
266 kind.span(),
267 "`external_subcommand` is allowed only on enum variants"
268 ),
269
270 Kind::Subcommand(ty) => {
271 let subcmd_type = match (**ty, sub_type(&field.ty)) {
272 (Ty::Option, Some(sub_type)) => sub_type,
273 _ => &field.ty,
274 };
275 let unwrapper = match **ty {
276 Ty::Option => quote!(),
277 _ => quote_spanned!( ty.span()=> .unwrap() ),
278 };
279 quote_spanned! { kind.span()=>
280 #field_name: <#subcmd_type as ::structopt::StructOptInternal>::from_subcommand(
281 #matches.subcommand())
282 #unwrapper
283 }
284 }
285
286 Kind::Flatten => quote_spanned! { kind.span()=>
287 #field_name: ::structopt::StructOpt::from_clap(#matches)
288 },
289
290 Kind::Skip(val) => match val {
291 None => quote_spanned!(kind.span()=> #field_name: Default::default()),
292 Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()),
293 },
294
295 Kind::Arg(ty) => {
296 use crate::attrs::ParserKind::*;
297
298 let parser = attrs.parser();
299 let func = &parser.func;
300 let span = parser.kind.span();
301 let (value_of, values_of, parse) = match *parser.kind {
302 FromStr => (
303 quote_spanned!(span=> value_of),
304 quote_spanned!(span=> values_of),
305 func.clone(),
306 ),
307 TryFromStr => (
308 quote_spanned!(span=> value_of),
309 quote_spanned!(span=> values_of),
310 quote_spanned!(func.span()=> |s| #func(s).unwrap()),
311 ),
312 FromOsStr => (
313 quote_spanned!(span=> value_of_os),
314 quote_spanned!(span=> values_of_os),
315 func.clone(),
316 ),
317 TryFromOsStr => (
318 quote_spanned!(span=> value_of_os),
319 quote_spanned!(span=> values_of_os),
320 quote_spanned!(func.span()=> |s| #func(s).unwrap()),
321 ),
322 FromOccurrences => (
323 quote_spanned!(span=> occurrences_of),
324 quote!(),
325 func.clone(),
326 ),
327 FromFlag => (quote!(), quote!(), func.clone()),
328 };
329
330 let flag = *attrs.parser().kind == ParserKind::FromFlag;
331 let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
332 let name = attrs.cased_name();
333 let field_value = match **ty {
334 Ty::Bool => quote_spanned!(ty.span()=> #matches.is_present(#name)),
335
336 Ty::Option => quote_spanned! { ty.span()=>
337 #matches.#value_of(#name)
338 .map(#parse)
339 },
340
341 Ty::OptionOption => quote_spanned! { ty.span()=>
342 if #matches.is_present(#name) {
343 Some(#matches.#value_of(#name).map(#parse))
344 } else {
345 None
346 }
347 },
348
349 Ty::OptionVec => quote_spanned! { ty.span()=>
350 if #matches.is_present(#name) {
351 Some(#matches.#values_of(#name)
352 .map_or_else(Vec::new, |v| v.map(#parse).collect()))
353 } else {
354 None
355 }
356 },
357
358 Ty::Vec => quote_spanned! { ty.span()=>
359 #matches.#values_of(#name)
360 .map_or_else(Vec::new, |v| v.map(#parse).collect())
361 },
362
363 Ty::Other if occurrences => quote_spanned! { ty.span()=>
364 #parse(#matches.#value_of(#name))
365 },
366
367 Ty::Other if flag => quote_spanned! { ty.span()=>
368 #parse(#matches.is_present(#name))
369 },
370
371 Ty::Other => quote_spanned! { ty.span()=>
372 #matches.#value_of(#name)
373 .map(#parse)
374 .unwrap()
375 },
376 };
377
378 quote_spanned!(field.span()=> #field_name: #field_value )
379 }
380 }
381 });
382
383 quote! {{
384 #( #fields ),*
385 }}
386 }
387
gen_from_clap( struct_name: &Ident, fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs, ) -> TokenStream388 fn gen_from_clap(
389 struct_name: &Ident,
390 fields: &Punctuated<Field, Comma>,
391 parent_attribute: &Attrs,
392 ) -> TokenStream {
393 let field_block = gen_constructor(fields, parent_attribute);
394
395 quote! {
396 fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
397 #struct_name #field_block
398 }
399 }
400 }
401
gen_clap(attrs: &[Attribute]) -> GenOutput402 fn gen_clap(attrs: &[Attribute]) -> GenOutput {
403 let name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
404
405 let attrs = Attrs::from_struct(
406 Span::call_site(),
407 attrs,
408 Name::Assigned(quote!(#name)),
409 None,
410 Sp::call_site(DEFAULT_CASING),
411 Sp::call_site(DEFAULT_ENV_CASING),
412 );
413 let tokens = {
414 let name = attrs.cased_name();
415 quote!(::structopt::clap::App::new(#name))
416 };
417
418 GenOutput { tokens, attrs }
419 }
420
gen_clap_struct(struct_attrs: &[Attribute]) -> GenOutput421 fn gen_clap_struct(struct_attrs: &[Attribute]) -> GenOutput {
422 let initial_clap_app_gen = gen_clap(struct_attrs);
423 let clap_tokens = initial_clap_app_gen.tokens;
424
425 let augmented_tokens = quote! {
426 fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
427 let app = #clap_tokens;
428 <Self as ::structopt::StructOptInternal>::augment_clap(app)
429 }
430 };
431
432 GenOutput {
433 tokens: augmented_tokens,
434 attrs: initial_clap_app_gen.attrs,
435 }
436 }
437
gen_augment_clap(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream438 fn gen_augment_clap(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
439 let app_var = Ident::new("app", Span::call_site());
440 let augmentation = gen_augmentation(fields, &app_var, parent_attribute);
441 quote! {
442 fn augment_clap<'a, 'b>(
443 #app_var: ::structopt::clap::App<'a, 'b>
444 ) -> ::structopt::clap::App<'a, 'b> {
445 #augmentation
446 }
447 }
448 }
449
gen_clap_enum(enum_attrs: &[Attribute]) -> GenOutput450 fn gen_clap_enum(enum_attrs: &[Attribute]) -> GenOutput {
451 let initial_clap_app_gen = gen_clap(enum_attrs);
452 let clap_tokens = initial_clap_app_gen.tokens;
453
454 let tokens = quote! {
455 fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
456 let app = #clap_tokens
457 .setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp);
458 <Self as ::structopt::StructOptInternal>::augment_clap(app)
459 }
460 };
461
462 GenOutput {
463 tokens,
464 attrs: initial_clap_app_gen.attrs,
465 }
466 }
467
gen_augment_clap_enum( variants: &Punctuated<Variant, Comma>, parent_attribute: &Attrs, ) -> TokenStream468 fn gen_augment_clap_enum(
469 variants: &Punctuated<Variant, Comma>,
470 parent_attribute: &Attrs,
471 ) -> TokenStream {
472 use syn::Fields::*;
473
474 let subcommands = variants.iter().map(|variant| {
475 let attrs = Attrs::from_struct(
476 variant.span(),
477 &variant.attrs,
478 Name::Derived(variant.ident.clone()),
479 Some(parent_attribute),
480 parent_attribute.casing(),
481 parent_attribute.env_casing(),
482 );
483
484 let kind = attrs.kind();
485 match &*kind {
486 Kind::ExternalSubcommand => {
487 let app_var = Ident::new("app", Span::call_site());
488 quote_spanned! { attrs.kind().span()=>
489 let #app_var = #app_var.setting(
490 ::structopt::clap::AppSettings::AllowExternalSubcommands
491 );
492 }
493 },
494
495 Kind::Flatten => {
496 match variant.fields {
497 Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
498 let ty = &unnamed[0];
499 quote! {
500 let app = <#ty as ::structopt::StructOptInternal>::augment_clap(app);
501 }
502 },
503 _ => abort!(
504 variant,
505 "`flatten` is usable only with single-typed tuple variants"
506 ),
507 }
508 },
509
510 _ => {
511 let app_var = Ident::new("subcommand", Span::call_site());
512 let arg_block = match variant.fields {
513 Named(ref fields) => gen_augmentation(&fields.named, &app_var, &attrs),
514 Unit => quote!( #app_var ),
515 Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
516 let ty = &unnamed[0];
517 quote_spanned! { ty.span()=>
518 {
519 let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap(
520 #app_var
521 );
522 if <#ty as ::structopt::StructOptInternal>::is_subcommand() {
523 #app_var.setting(
524 ::structopt::clap::AppSettings::SubcommandRequiredElseHelp
525 )
526 } else {
527 #app_var
528 }
529 }
530 }
531 }
532 Unnamed(..) => abort!(variant, "non single-typed tuple enums are not supported"),
533 };
534
535 let name = attrs.cased_name();
536 let from_attrs = attrs.top_level_methods();
537 let version = attrs.version();
538 quote! {
539 let app = app.subcommand({
540 let #app_var = ::structopt::clap::SubCommand::with_name(#name);
541 let #app_var = #arg_block;
542 #app_var#from_attrs#version
543 });
544 }
545 },
546 }
547 });
548
549 let app_methods = parent_attribute.top_level_methods();
550 let version = parent_attribute.version();
551 quote! {
552 fn augment_clap<'a, 'b>(
553 app: ::structopt::clap::App<'a, 'b>
554 ) -> ::structopt::clap::App<'a, 'b> {
555 let app = app #app_methods;
556 #( #subcommands )*;
557 app #version
558 }
559 }
560 }
561
gen_from_clap_enum(name: &Ident) -> TokenStream562 fn gen_from_clap_enum(name: &Ident) -> TokenStream {
563 quote! {
564 fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
565 <#name as ::structopt::StructOptInternal>::from_subcommand(matches.subcommand())
566 .expect("structopt misuse: You likely tried to #[flatten] a struct \
567 that contains #[subcommand]. This is forbidden.")
568 }
569 }
570 }
571
gen_from_subcommand( name: &Ident, variants: &Punctuated<Variant, Comma>, parent_attribute: &Attrs, ) -> TokenStream572 fn gen_from_subcommand(
573 name: &Ident,
574 variants: &Punctuated<Variant, Comma>,
575 parent_attribute: &Attrs,
576 ) -> TokenStream {
577 use syn::Fields::*;
578
579 let mut ext_subcmd = None;
580
581 let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
582 .iter()
583 .filter_map(|variant| {
584 let attrs = Attrs::from_struct(
585 variant.span(),
586 &variant.attrs,
587 Name::Derived(variant.ident.clone()),
588 Some(parent_attribute),
589 parent_attribute.casing(),
590 parent_attribute.env_casing(),
591 );
592
593 let variant_name = &variant.ident;
594
595 if let Kind::ExternalSubcommand = *attrs.kind() {
596 if ext_subcmd.is_some() {
597 abort!(
598 attrs.kind().span(),
599 "Only one variant can be marked with `external_subcommand`, \
600 this is the second"
601 );
602 }
603
604 let ty = match variant.fields {
605 Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
606
607 _ => abort!(
608 variant,
609 "The enum variant marked with `external_attribute` must be \
610 a single-typed tuple, and the type must be either `Vec<String>` \
611 or `Vec<OsString>`."
612 ),
613 };
614
615 let (span, str_ty, values_of) = match subty_if_name(ty, "Vec") {
616 Some(subty) => {
617 if is_simple_ty(subty, "String") {
618 (
619 subty.span(),
620 quote!(::std::string::String),
621 quote!(values_of),
622 )
623 } else {
624 (
625 subty.span(),
626 quote!(::std::ffi::OsString),
627 quote!(values_of_os),
628 )
629 }
630 }
631
632 None => abort!(
633 ty,
634 "The type must be either `Vec<String>` or `Vec<OsString>` \
635 to be used with `external_subcommand`."
636 ),
637 };
638
639 ext_subcmd = Some((span, variant_name, str_ty, values_of));
640 None
641 } else {
642 Some((variant, attrs))
643 }
644 })
645 .partition(|(_, attrs)| match &*attrs.kind() {
646 Kind::Flatten => true,
647 _ => false,
648 });
649
650 let other = format_ident!("other");
651 let matches = format_ident!("matches");
652
653 let external = match ext_subcmd {
654 Some((span, var_name, str_ty, values_of)) => quote_spanned! { span=>
655 match #other {
656 ("", ::std::option::Option::None) => None,
657
658 (external, Some(#matches)) => {
659 ::std::option::Option::Some(#name::#var_name(
660 ::std::iter::once(#str_ty::from(external))
661 .chain(
662 #matches.#values_of("").into_iter().flatten().map(#str_ty::from)
663 )
664 .collect::<::std::vec::Vec<_>>()
665 ))
666 }
667
668 (external, None) => {
669 ::std::option::Option::Some(#name::#var_name(
670 ::std::iter::once(#str_ty::from(external))
671 .collect::<::std::vec::Vec<_>>()
672 ))
673 }
674 }
675 },
676
677 None => quote!(None),
678 };
679
680 let match_arms = variants.iter().map(|(variant, attrs)| {
681 let sub_name = attrs.cased_name();
682 let variant_name = &variant.ident;
683 let constructor_block = match variant.fields {
684 Named(ref fields) => gen_constructor(&fields.named, &attrs),
685 Unit => quote!(),
686 Unnamed(ref fields) if fields.unnamed.len() == 1 => {
687 let ty = &fields.unnamed[0];
688 quote!( ( <#ty as ::structopt::StructOpt>::from_clap(#matches) ) )
689 }
690 Unnamed(..) => abort!(
691 variant.ident,
692 "non single-typed tuple enums are not supported"
693 ),
694 };
695
696 quote! {
697 (#sub_name, Some(#matches)) => {
698 Some(#name :: #variant_name #constructor_block)
699 }
700 }
701 });
702
703 let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| {
704 let variant_name = &variant.ident;
705 match variant.fields {
706 Unnamed(ref fields) if fields.unnamed.len() == 1 => {
707 let ty = &fields.unnamed[0];
708 quote! {
709 if let Some(res) =
710 <#ty as ::structopt::StructOptInternal>::from_subcommand(#other)
711 {
712 return Some(#name :: #variant_name (res));
713 }
714 }
715 }
716 _ => abort!(
717 variant,
718 "`flatten` is usable only with single-typed tuple variants"
719 ),
720 }
721 });
722
723 quote! {
724 fn from_subcommand<'a, 'b>(
725 sub: (&'b str, Option<&'b ::structopt::clap::ArgMatches<'a>>)
726 ) -> Option<Self> {
727 match sub {
728 #( #match_arms, )*
729 #other => {
730 #( #child_subcommands )else*;
731 #external
732 }
733 }
734 }
735 }
736 }
737
738 #[cfg(feature = "paw")]
gen_paw_impl(name: &Ident) -> TokenStream739 fn gen_paw_impl(name: &Ident) -> TokenStream {
740 quote! {
741 impl ::structopt::paw::ParseArgs for #name {
742 type Error = std::io::Error;
743
744 fn parse_args() -> std::result::Result<Self, Self::Error> {
745 Ok(<#name as ::structopt::StructOpt>::from_args())
746 }
747 }
748 }
749 }
750 #[cfg(not(feature = "paw"))]
gen_paw_impl(_: &Ident) -> TokenStream751 fn gen_paw_impl(_: &Ident) -> TokenStream {
752 TokenStream::new()
753 }
754
impl_structopt_for_struct( name: &Ident, fields: &Punctuated<Field, Comma>, attrs: &[Attribute], ) -> TokenStream755 fn impl_structopt_for_struct(
756 name: &Ident,
757 fields: &Punctuated<Field, Comma>,
758 attrs: &[Attribute],
759 ) -> TokenStream {
760 let basic_clap_app_gen = gen_clap_struct(attrs);
761 let augment_clap = gen_augment_clap(fields, &basic_clap_app_gen.attrs);
762 let from_clap = gen_from_clap(name, fields, &basic_clap_app_gen.attrs);
763 let paw_impl = gen_paw_impl(name);
764
765 let clap_tokens = basic_clap_app_gen.tokens;
766 quote! {
767 #[allow(unused_variables)]
768 #[allow(unknown_lints)]
769 #[allow(
770 clippy::style,
771 clippy::complexity,
772 clippy::pedantic,
773 clippy::restriction,
774 clippy::perf,
775 clippy::deprecated,
776 clippy::nursery,
777 clippy::cargo
778 )]
779 #[deny(clippy::correctness)]
780 #[allow(dead_code, unreachable_code)]
781 impl ::structopt::StructOpt for #name {
782 #clap_tokens
783 #from_clap
784 }
785
786 #[allow(unused_variables)]
787 #[allow(unknown_lints)]
788 #[allow(
789 clippy::style,
790 clippy::complexity,
791 clippy::pedantic,
792 clippy::restriction,
793 clippy::perf,
794 clippy::deprecated,
795 clippy::nursery,
796 clippy::cargo
797 )]
798 #[deny(clippy::correctness)]
799 #[allow(dead_code, unreachable_code)]
800 impl ::structopt::StructOptInternal for #name {
801 #augment_clap
802 fn is_subcommand() -> bool { false }
803 }
804
805 #paw_impl
806 }
807 }
808
impl_structopt_for_enum( name: &Ident, variants: &Punctuated<Variant, Comma>, attrs: &[Attribute], ) -> TokenStream809 fn impl_structopt_for_enum(
810 name: &Ident,
811 variants: &Punctuated<Variant, Comma>,
812 attrs: &[Attribute],
813 ) -> TokenStream {
814 let basic_clap_app_gen = gen_clap_enum(attrs);
815 let clap_tokens = basic_clap_app_gen.tokens;
816 let attrs = basic_clap_app_gen.attrs;
817
818 let augment_clap = gen_augment_clap_enum(variants, &attrs);
819 let from_clap = gen_from_clap_enum(name);
820 let from_subcommand = gen_from_subcommand(name, variants, &attrs);
821 let paw_impl = gen_paw_impl(name);
822
823 quote! {
824 #[allow(unknown_lints)]
825 #[allow(unused_variables, dead_code, unreachable_code)]
826 #[allow(
827 clippy::style,
828 clippy::complexity,
829 clippy::pedantic,
830 clippy::restriction,
831 clippy::perf,
832 clippy::deprecated,
833 clippy::nursery,
834 clippy::cargo
835 )]
836 #[deny(clippy::correctness)]
837 impl ::structopt::StructOpt for #name {
838 #clap_tokens
839 #from_clap
840 }
841
842 #[allow(unused_variables)]
843 #[allow(unknown_lints)]
844 #[allow(
845 clippy::style,
846 clippy::complexity,
847 clippy::pedantic,
848 clippy::restriction,
849 clippy::perf,
850 clippy::deprecated,
851 clippy::nursery,
852 clippy::cargo
853 )]
854 #[deny(clippy::correctness)]
855 #[allow(dead_code, unreachable_code)]
856 impl ::structopt::StructOptInternal for #name {
857 #augment_clap
858 #from_subcommand
859 fn is_subcommand() -> bool { true }
860 }
861
862 #paw_impl
863 }
864 }
865
impl_structopt(input: &DeriveInput) -> TokenStream866 fn impl_structopt(input: &DeriveInput) -> TokenStream {
867 use syn::Data::*;
868
869 let struct_name = &input.ident;
870
871 set_dummy(quote! {
872 impl ::structopt::StructOpt for #struct_name {
873 fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
874 unimplemented!()
875 }
876 fn from_clap(_matches: &::structopt::clap::ArgMatches) -> Self {
877 unimplemented!()
878 }
879 }
880
881 impl ::structopt::StructOptInternal for #struct_name {}
882 });
883
884 match input.data {
885 Struct(DataStruct {
886 fields: syn::Fields::Named(ref fields),
887 ..
888 }) => impl_structopt_for_struct(struct_name, &fields.named, &input.attrs),
889 Enum(ref e) => impl_structopt_for_enum(struct_name, &e.variants, &input.attrs),
890 _ => abort_call_site!("structopt only supports non-tuple structs and enums"),
891 }
892 }
893