structopt_derive/
lib.rs

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
18extern crate proc_macro;
19
20mod attrs;
21mod doc_comments;
22mod parse;
23mod spanned;
24mod ty;
25
26use crate::{
27    attrs::{Attrs, CasingStyle, Kind, Name, ParserKind},
28    spanned::Sp,
29    ty::{is_simple_ty, sub_type, subty_if_name, Ty},
30};
31
32use proc_macro2::{Span, TokenStream};
33use proc_macro_error::{abort, abort_call_site, proc_macro_error, set_dummy};
34use quote::{format_ident, quote, quote_spanned};
35use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, *};
36
37/// Default casing style for generated arguments.
38const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
39
40/// Default casing style for environment variables
41const 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.
48struct 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]
56pub 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.
64fn 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
244fn 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 convert_type = match **ty {
334                    Ty::Vec | Ty::Option => sub_type(&field.ty).unwrap_or(&field.ty),
335                    Ty::OptionOption | Ty::OptionVec => {
336                        sub_type(&field.ty).and_then(sub_type).unwrap_or(&field.ty)
337                    }
338                    _ => &field.ty,
339                };
340                let field_value = match **ty {
341                    Ty::Bool => quote_spanned!(ty.span()=> #matches.is_present(#name)),
342
343                    Ty::Option => quote_spanned! { ty.span()=>
344                        #matches.#value_of(#name)
345                            .map(#parse)
346                    },
347
348                    Ty::OptionOption => quote_spanned! { ty.span()=>
349                        if #matches.is_present(#name) {
350                            Some(#matches.#value_of(#name).map(#parse))
351                        } else {
352                            None
353                        }
354                    },
355
356                    Ty::OptionVec => quote_spanned! { ty.span()=>
357                        if #matches.is_present(#name) {
358                            Some(#matches.#values_of(#name)
359                                 .map_or_else(Vec::new, |v| v.map::<#convert_type, _>(#parse).collect()))
360                        } else {
361                            None
362                        }
363                    },
364
365                    Ty::Vec => quote_spanned! { ty.span()=>
366                        #matches.#values_of(#name)
367                            .map_or_else(Vec::new, |v| v.map::<#convert_type, _>(#parse).collect())
368                    },
369
370                    Ty::Other if occurrences => quote_spanned! { ty.span()=>
371                        #parse(#matches.#value_of(#name))
372                    },
373
374                    Ty::Other if flag => quote_spanned! { ty.span()=>
375                        #parse(#matches.is_present(#name))
376                    },
377
378                    Ty::Other => quote_spanned! { ty.span()=>
379                        #matches.#value_of(#name)
380                            .map(#parse)
381                            .unwrap()
382                    },
383                };
384
385                quote_spanned!(field.span()=> #field_name: #field_value )
386            }
387        }
388    });
389
390    quote! {{
391        #( #fields ),*
392    }}
393}
394
395fn gen_from_clap(
396    struct_name: &Ident,
397    fields: &Punctuated<Field, Comma>,
398    parent_attribute: &Attrs,
399) -> TokenStream {
400    let field_block = gen_constructor(fields, parent_attribute);
401
402    quote! {
403        fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
404            #struct_name #field_block
405        }
406    }
407}
408
409fn gen_clap(attrs: &[Attribute]) -> GenOutput {
410    let name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
411
412    let attrs = Attrs::from_struct(
413        Span::call_site(),
414        attrs,
415        Name::Assigned(quote!(#name)),
416        None,
417        Sp::call_site(DEFAULT_CASING),
418        Sp::call_site(DEFAULT_ENV_CASING),
419        false,
420    );
421    let tokens = {
422        let name = attrs.cased_name();
423        quote!(::structopt::clap::App::new(#name))
424    };
425
426    GenOutput { tokens, attrs }
427}
428
429fn gen_clap_struct(struct_attrs: &[Attribute]) -> GenOutput {
430    let initial_clap_app_gen = gen_clap(struct_attrs);
431    let clap_tokens = initial_clap_app_gen.tokens;
432
433    let augmented_tokens = quote! {
434        fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
435            let app = #clap_tokens;
436            <Self as ::structopt::StructOptInternal>::augment_clap(app)
437        }
438    };
439
440    GenOutput {
441        tokens: augmented_tokens,
442        attrs: initial_clap_app_gen.attrs,
443    }
444}
445
446fn gen_augment_clap(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
447    let app_var = Ident::new("app", Span::call_site());
448    let augmentation = gen_augmentation(fields, &app_var, parent_attribute);
449    quote! {
450        fn augment_clap<'a, 'b>(
451            #app_var: ::structopt::clap::App<'a, 'b>
452        ) -> ::structopt::clap::App<'a, 'b> {
453            #augmentation
454        }
455    }
456}
457
458fn gen_clap_enum(enum_attrs: &[Attribute]) -> GenOutput {
459    let initial_clap_app_gen = gen_clap(enum_attrs);
460    let clap_tokens = initial_clap_app_gen.tokens;
461
462    let tokens = quote! {
463        fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
464            let app = #clap_tokens
465                .setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp);
466            <Self as ::structopt::StructOptInternal>::augment_clap(app)
467        }
468    };
469
470    GenOutput {
471        tokens,
472        attrs: initial_clap_app_gen.attrs,
473    }
474}
475
476fn gen_augment_clap_enum(
477    variants: &Punctuated<Variant, Comma>,
478    parent_attribute: &Attrs,
479) -> TokenStream {
480    use syn::Fields::*;
481
482    let subcommands = variants.iter().filter_map(|variant| {
483        let attrs = Attrs::from_struct(
484            variant.span(),
485            &variant.attrs,
486            Name::Derived(variant.ident.clone()),
487            Some(parent_attribute),
488            parent_attribute.casing(),
489            parent_attribute.env_casing(),
490            true,
491        );
492
493        let kind = attrs.kind();
494        match &*kind {
495            Kind::Skip(_) => None,
496
497            Kind::ExternalSubcommand => {
498                let app_var = Ident::new("app", Span::call_site());
499                Some(quote_spanned! { attrs.kind().span()=>
500                    let #app_var = #app_var.setting(
501                        ::structopt::clap::AppSettings::AllowExternalSubcommands
502                    );
503                })
504            },
505
506            Kind::Flatten => {
507                match variant.fields {
508                    Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
509                        let ty = &unnamed[0];
510                        Some(quote! {
511                            let app = <#ty as ::structopt::StructOptInternal>::augment_clap(app);
512                        })
513                    },
514                    _ => abort!(
515                        variant,
516                        "`flatten` is usable only with single-typed tuple variants"
517                    ),
518                }
519            },
520
521            _ => {
522                let app_var = Ident::new("subcommand", Span::call_site());
523                let from_attrs = attrs.top_level_methods();
524                let version = attrs.version();
525
526                let arg_block = match variant.fields {
527                    // If the variant is named, then gen_augmentation already generates the
528                    // top level methods (#from_attrs) and version.
529                    Named(ref fields) => gen_augmentation(&fields.named, &app_var, &attrs),
530                    Unit => quote!( #app_var#from_attrs#version ),
531                    Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
532                        let ty = &unnamed[0];
533                        quote_spanned! { ty.span()=>
534                            {
535                                let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap(
536                                    #app_var
537                                );
538                                if <#ty as ::structopt::StructOptInternal>::is_subcommand() {
539                                    #app_var.setting(
540                                        ::structopt::clap::AppSettings::SubcommandRequiredElseHelp
541                                    )
542                                } else {
543                                    #app_var
544                                }#from_attrs#version
545                            }
546                        }
547                    }
548                    Unnamed(..) => abort!(variant, "non single-typed tuple enums are not supported"),
549                };
550
551                let name = attrs.cased_name();
552                Some(quote! {
553                    let app = app.subcommand({
554                        let #app_var = ::structopt::clap::SubCommand::with_name(#name);
555                        #arg_block
556                    });
557                })
558            },
559        }
560    });
561
562    let app_methods = parent_attribute.top_level_methods();
563    let version = parent_attribute.version();
564    quote! {
565        fn augment_clap<'a, 'b>(
566            app: ::structopt::clap::App<'a, 'b>
567        ) -> ::structopt::clap::App<'a, 'b> {
568            let app = app #app_methods;
569            #( #subcommands )*;
570            app #version
571        }
572    }
573}
574
575fn gen_from_clap_enum() -> TokenStream {
576    quote! {
577        fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
578            <Self as ::structopt::StructOptInternal>::from_subcommand(matches.subcommand())
579                .expect("structopt misuse: You likely tried to #[flatten] a struct \
580                         that contains #[subcommand]. This is forbidden.")
581        }
582    }
583}
584
585fn gen_from_subcommand(
586    name: &Ident,
587    variants: &Punctuated<Variant, Comma>,
588    parent_attribute: &Attrs,
589) -> TokenStream {
590    use syn::Fields::*;
591
592    let mut ext_subcmd = None;
593
594    let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
595        .iter()
596        .filter_map(|variant| {
597            let attrs = Attrs::from_struct(
598                variant.span(),
599                &variant.attrs,
600                Name::Derived(variant.ident.clone()),
601                Some(parent_attribute),
602                parent_attribute.casing(),
603                parent_attribute.env_casing(),
604                true,
605            );
606
607            let variant_name = &variant.ident;
608
609            match *attrs.kind() {
610                Kind::ExternalSubcommand => {
611                    if ext_subcmd.is_some() {
612                        abort!(
613                            attrs.kind().span(),
614                            "Only one variant can be marked with `external_subcommand`, \
615                         this is the second"
616                        );
617                    }
618
619                    let ty = match variant.fields {
620                        Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
621
622                        _ => abort!(
623                            variant,
624                            "The enum variant marked with `external_attribute` must be \
625                         a single-typed tuple, and the type must be either `Vec<String>` \
626                         or `Vec<OsString>`."
627                        ),
628                    };
629
630                    let (span, str_ty, values_of) = match subty_if_name(ty, "Vec") {
631                        Some(subty) => {
632                            if is_simple_ty(subty, "String") {
633                                (
634                                    subty.span(),
635                                    quote!(::std::string::String),
636                                    quote!(values_of),
637                                )
638                            } else {
639                                (
640                                    subty.span(),
641                                    quote!(::std::ffi::OsString),
642                                    quote!(values_of_os),
643                                )
644                            }
645                        }
646
647                        None => abort!(
648                            ty,
649                            "The type must be either `Vec<String>` or `Vec<OsString>` \
650                         to be used with `external_subcommand`."
651                        ),
652                    };
653
654                    ext_subcmd = Some((span, variant_name, str_ty, values_of));
655                    None
656                }
657                Kind::Skip(_) => None,
658                _ => Some((variant, attrs)),
659            }
660        })
661        .partition(|(_, attrs)| match &*attrs.kind() {
662            Kind::Flatten => true,
663            _ => false,
664        });
665
666    let other = format_ident!("other");
667    let matches = format_ident!("matches");
668
669    let external = match ext_subcmd {
670        Some((span, var_name, str_ty, values_of)) => quote_spanned! { span=>
671            match #other {
672                ("", ::std::option::Option::None) => None,
673
674                (external, Some(#matches)) => {
675                    ::std::option::Option::Some(#name::#var_name(
676                        ::std::iter::once(#str_ty::from(external))
677                        .chain(
678                            #matches.#values_of("").into_iter().flatten().map(#str_ty::from)
679                        )
680                        .collect::<::std::vec::Vec<_>>()
681                    ))
682                }
683
684                (external, None) => {
685                    ::std::option::Option::Some(#name::#var_name(
686                        ::std::iter::once(#str_ty::from(external))
687                            .collect::<::std::vec::Vec<_>>()
688                    ))
689                }
690            }
691        },
692
693        None => quote!(None),
694    };
695
696    let match_arms = variants.iter().map(|(variant, attrs)| {
697        let sub_name = attrs.cased_name();
698        let variant_name = &variant.ident;
699        let constructor_block = match variant.fields {
700            Named(ref fields) => gen_constructor(&fields.named, &attrs),
701            Unit => quote!(),
702            Unnamed(ref fields) if fields.unnamed.len() == 1 => {
703                let ty = &fields.unnamed[0];
704                quote!( ( <#ty as ::structopt::StructOpt>::from_clap(#matches) ) )
705            }
706            Unnamed(..) => abort!(
707                variant.ident,
708                "non single-typed tuple enums are not supported"
709            ),
710        };
711
712        quote! {
713            (#sub_name, Some(#matches)) => {
714                Some(#name :: #variant_name #constructor_block)
715            }
716        }
717    });
718
719    let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| {
720        let variant_name = &variant.ident;
721        match variant.fields {
722            Unnamed(ref fields) if fields.unnamed.len() == 1 => {
723                let ty = &fields.unnamed[0];
724                quote! {
725                    if let Some(res) =
726                        <#ty as ::structopt::StructOptInternal>::from_subcommand(#other)
727                    {
728                        return Some(#name :: #variant_name (res));
729                    }
730                }
731            }
732            _ => abort!(
733                variant,
734                "`flatten` is usable only with single-typed tuple variants"
735            ),
736        }
737    });
738
739    quote! {
740        fn from_subcommand<'a, 'b>(
741            sub: (&'b str, Option<&'b ::structopt::clap::ArgMatches<'a>>)
742        ) -> Option<Self> {
743            match sub {
744                #( #match_arms, )*
745                #other => {
746                    #( #child_subcommands )else*;
747                    #external
748                }
749            }
750        }
751    }
752}
753
754#[cfg(feature = "paw")]
755fn gen_paw_impl(
756    impl_generics: &ImplGenerics,
757    name: &Ident,
758    ty_generics: &TypeGenerics,
759    where_clause: &TokenStream,
760) -> TokenStream {
761    quote! {
762        impl #impl_generics ::structopt::paw::ParseArgs for #name #ty_generics #where_clause {
763            type Error = std::io::Error;
764
765            fn parse_args() -> std::result::Result<Self, Self::Error> {
766                Ok(<#name as ::structopt::StructOpt>::from_args())
767            }
768        }
769    }
770}
771#[cfg(not(feature = "paw"))]
772fn gen_paw_impl(_: &ImplGenerics, _: &Ident, _: &TypeGenerics, _: &TokenStream) -> TokenStream {
773    TokenStream::new()
774}
775
776fn split_structopt_generics_for_impl(
777    generics: &Generics,
778) -> (ImplGenerics, TypeGenerics, TokenStream) {
779    use syn::{token::Add, TypeParamBound::Trait};
780
781    fn path_ends_with(path: &Path, ident: &str) -> bool {
782        path.segments.last().unwrap().ident == ident
783    }
784
785    fn type_param_bounds_contains(bounds: &Punctuated<TypeParamBound, Add>, ident: &str) -> bool {
786        for bound in bounds {
787            if let Trait(bound) = bound {
788                if path_ends_with(&bound.path, ident) {
789                    return true;
790                }
791            }
792        }
793        return false;
794    }
795
796    struct TraitBoundAmendments {
797        tokens: TokenStream,
798        need_where: bool,
799        need_comma: bool,
800    }
801
802    impl TraitBoundAmendments {
803        fn new(where_clause: Option<&WhereClause>) -> Self {
804            let tokens = TokenStream::new();
805            let (need_where, need_comma) = if let Some(where_clause) = where_clause {
806                if where_clause.predicates.trailing_punct() {
807                    (false, false)
808                } else {
809                    (false, true)
810                }
811            } else {
812                (true, false)
813            };
814            Self {
815                tokens,
816                need_where,
817                need_comma,
818            }
819        }
820
821        fn add(&mut self, amendment: TokenStream) {
822            if self.need_where {
823                self.tokens.extend(quote! { where });
824                self.need_where = false;
825            }
826            if self.need_comma {
827                self.tokens.extend(quote! { , });
828            }
829            self.tokens.extend(amendment);
830            self.need_comma = true;
831        }
832
833        fn into_tokens(self) -> TokenStream {
834            self.tokens
835        }
836    }
837
838    let mut trait_bound_amendments = TraitBoundAmendments::new(generics.where_clause.as_ref());
839
840    for param in &generics.params {
841        if let GenericParam::Type(param) = param {
842            let param_ident = &param.ident;
843            if type_param_bounds_contains(&param.bounds, "StructOpt") {
844                trait_bound_amendments
845                    .add(quote! { #param_ident : ::structopt::StructOptInternal });
846            }
847        }
848    }
849
850    if let Some(where_clause) = &generics.where_clause {
851        for predicate in &where_clause.predicates {
852            if let WherePredicate::Type(predicate) = predicate {
853                let predicate_bounded_ty = &predicate.bounded_ty;
854                if type_param_bounds_contains(&predicate.bounds, "StructOpt") {
855                    trait_bound_amendments
856                        .add(quote! { #predicate_bounded_ty : ::structopt::StructOptInternal });
857                }
858            }
859        }
860    }
861
862    let trait_bound_amendments = trait_bound_amendments.into_tokens();
863
864    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
865
866    let where_clause = quote! { #where_clause #trait_bound_amendments };
867
868    (impl_generics, ty_generics, where_clause)
869}
870
871fn impl_structopt_for_struct(
872    name: &Ident,
873    fields: &Punctuated<Field, Comma>,
874    attrs: &[Attribute],
875    generics: &Generics,
876) -> TokenStream {
877    let (impl_generics, ty_generics, where_clause) = split_structopt_generics_for_impl(&generics);
878
879    let basic_clap_app_gen = gen_clap_struct(attrs);
880    let augment_clap = gen_augment_clap(fields, &basic_clap_app_gen.attrs);
881    let from_clap = gen_from_clap(name, fields, &basic_clap_app_gen.attrs);
882    let paw_impl = gen_paw_impl(&impl_generics, name, &ty_generics, &where_clause);
883
884    let clap_tokens = basic_clap_app_gen.tokens;
885    quote! {
886        #[allow(unused_variables)]
887        #[allow(unknown_lints)]
888        #[allow(
889            clippy::style,
890            clippy::complexity,
891            clippy::pedantic,
892            clippy::restriction,
893            clippy::perf,
894            clippy::deprecated,
895            clippy::nursery,
896            clippy::cargo
897        )]
898        #[deny(clippy::correctness)]
899        #[allow(dead_code, unreachable_code)]
900        impl #impl_generics ::structopt::StructOpt for #name #ty_generics #where_clause {
901            #clap_tokens
902            #from_clap
903        }
904
905        #[allow(unused_variables)]
906        #[allow(unknown_lints)]
907        #[allow(
908            clippy::style,
909            clippy::complexity,
910            clippy::pedantic,
911            clippy::restriction,
912            clippy::perf,
913            clippy::deprecated,
914            clippy::nursery,
915            clippy::cargo
916        )]
917        #[deny(clippy::correctness)]
918        #[allow(dead_code, unreachable_code)]
919        impl #impl_generics ::structopt::StructOptInternal for #name #ty_generics #where_clause {
920            #augment_clap
921            fn is_subcommand() -> bool { false }
922        }
923
924        #paw_impl
925    }
926}
927
928fn impl_structopt_for_enum(
929    name: &Ident,
930    variants: &Punctuated<Variant, Comma>,
931    attrs: &[Attribute],
932    generics: &Generics,
933) -> TokenStream {
934    let (impl_generics, ty_generics, where_clause) = split_structopt_generics_for_impl(&generics);
935
936    let basic_clap_app_gen = gen_clap_enum(attrs);
937    let clap_tokens = basic_clap_app_gen.tokens;
938    let attrs = basic_clap_app_gen.attrs;
939
940    let augment_clap = gen_augment_clap_enum(variants, &attrs);
941    let from_clap = gen_from_clap_enum();
942    let from_subcommand = gen_from_subcommand(name, variants, &attrs);
943    let paw_impl = gen_paw_impl(&impl_generics, name, &ty_generics, &where_clause);
944
945    quote! {
946        #[allow(unknown_lints)]
947        #[allow(unused_variables, dead_code, unreachable_code)]
948        #[allow(
949            clippy::style,
950            clippy::complexity,
951            clippy::pedantic,
952            clippy::restriction,
953            clippy::perf,
954            clippy::deprecated,
955            clippy::nursery,
956            clippy::cargo
957        )]
958        #[deny(clippy::correctness)]
959        impl #impl_generics ::structopt::StructOpt for #name #ty_generics #where_clause {
960            #clap_tokens
961            #from_clap
962        }
963
964        #[allow(unused_variables)]
965        #[allow(unknown_lints)]
966        #[allow(
967            clippy::style,
968            clippy::complexity,
969            clippy::pedantic,
970            clippy::restriction,
971            clippy::perf,
972            clippy::deprecated,
973            clippy::nursery,
974            clippy::cargo
975        )]
976        #[deny(clippy::correctness)]
977        #[allow(dead_code, unreachable_code)]
978        impl #impl_generics ::structopt::StructOptInternal for #name #ty_generics #where_clause {
979            #augment_clap
980            #from_subcommand
981            fn is_subcommand() -> bool { true }
982        }
983
984        #paw_impl
985    }
986}
987
988fn impl_structopt(input: &DeriveInput) -> TokenStream {
989    use syn::Data::*;
990
991    let struct_name = &input.ident;
992
993    set_dummy(quote! {
994        impl ::structopt::StructOpt for #struct_name {
995            fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
996                unimplemented!()
997            }
998            fn from_clap(_matches: &::structopt::clap::ArgMatches) -> Self {
999                unimplemented!()
1000            }
1001        }
1002
1003        impl ::structopt::StructOptInternal for #struct_name {}
1004    });
1005
1006    match input.data {
1007        Struct(DataStruct {
1008            fields: syn::Fields::Named(ref fields),
1009            ..
1010        }) => impl_structopt_for_struct(struct_name, &fields.named, &input.attrs, &input.generics),
1011        Enum(ref e) => {
1012            impl_structopt_for_enum(struct_name, &e.variants, &input.attrs, &input.generics)
1013        }
1014        _ => abort_call_site!("structopt only supports non-tuple structs and enums"),
1015    }
1016}