Skip to main content

clap_derive/derives/
parser.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
15use proc_macro2::TokenStream;
16use quote::quote;
17use syn::Ident;
18use syn::Variant;
19use syn::{
20    self, Data, DataStruct, DeriveInput, Field, Fields, Generics, punctuated::Punctuated,
21    token::Comma,
22};
23
24use crate::derives::args::collect_args_fields;
25use crate::derives::{args, into_app, subcommand};
26use crate::item::Item;
27use crate::item::Name;
28
29pub(crate) fn derive_parser(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
30    let ident = &input.ident;
31    let pkg_name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
32
33    let pkg_name_tokens = if pkg_name.is_empty() {
34        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&pkg_name, &mut _s);
    _s
}quote!(#pkg_name)
35    } else {
36        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "let");
            ::quote::__private::push_underscore(&mut _s);
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "env");
            ::quote::__private::push_bang(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::parse(&mut _s, "\"CARGO_PKG_NAME\"");
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            ::quote::ToTokens::to_tokens(&pkg_name, &mut _s);
            _s
        });
    _s
}quote!({
37            let _ = ::core::env!("CARGO_PKG_NAME");
38            #pkg_name })
39    };
40
41    match input.data {
42        Data::Struct(DataStruct {
43            fields: Fields::Named(ref fields),
44            ..
45        }) => {
46            let name = Name::Assigned(pkg_name_tokens.clone());
47            let item = Item::from_args_struct(input, name)?;
48            let fields = collect_args_fields(&item, fields)?;
49            gen_for_struct(&item, ident, &input.generics, &fields)
50        }
51        Data::Struct(DataStruct {
52            fields: Fields::Unit,
53            ..
54        }) => {
55            let name = Name::Assigned(pkg_name_tokens.clone());
56            let item = Item::from_args_struct(input, name)?;
57            let fields = Punctuated::<Field, Comma>::new();
58            let fields = fields
59                .iter()
60                .map(|field| {
61                    let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
62                    Ok((field, item))
63                })
64                .collect::<Result<Vec<_>, syn::Error>>()?;
65            gen_for_struct(&item, ident, &input.generics, &fields)
66        }
67        Data::Enum(ref e) => {
68            let name = Name::Assigned(pkg_name_tokens);
69            let item = Item::from_subcommand_enum(input, name)?;
70            let variants = e
71                .variants
72                .iter()
73                .map(|variant| {
74                    let item =
75                        Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?;
76                    Ok((variant, item))
77                })
78                .collect::<Result<Vec<_>, syn::Error>>()?;
79            gen_for_enum(&item, ident, &input.generics, &variants)
80        }
81        _ => {
    let span = proc_macro2::Span::call_site();
    {
        return Err({
                    #[allow(unused_imports)]
                    use crate::utils::error::*;
                    let msg =
                        ::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("`#[derive(Parser)]` only supports non-tuple structs and enums"))
                            });
                    span.EXPECTED_Span_OR_ToTokens(msg)
                });
    }
}abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"),
82    }
83}
84
85fn gen_for_struct(
86    item: &Item,
87    item_name: &Ident,
88    generics: &Generics,
89    fields: &[(&Field, Item)],
90) -> Result<TokenStream, syn::Error> {
91    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
92
93    let into_app = into_app::gen_for_struct(item, item_name, generics)?;
94    let args = args::gen_for_struct(item, item_name, generics, fields)?;
95
96    Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "automatically_derived");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s,
                        "unused_qualifications");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "clippy");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "redundant_locals");
                    ::quote::__private::push_comma(&mut _s);
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_ident(&mut _s, "clap");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "Parser");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&item_name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        ::quote::__private::TokenStream::new());
    ::quote::ToTokens::to_tokens(&into_app, &mut _s);
    ::quote::ToTokens::to_tokens(&args, &mut _s);
    _s
}quote! {
97        #[automatically_derived]
98        #[allow(
99            unused_qualifications,
100            clippy::redundant_locals,
101        )]
102        impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
103
104        #into_app
105        #args
106    })
107}
108
109fn gen_for_enum(
110    item: &Item,
111    item_name: &Ident,
112    generics: &Generics,
113    variants: &[(&Variant, Item)],
114) -> Result<TokenStream, syn::Error> {
115    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
116
117    let into_app = into_app::gen_for_enum(item, item_name, generics)?;
118    let subcommand = subcommand::gen_for_enum(item, item_name, generics, variants)?;
119
120    Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "automatically_derived");
            _s
        });
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_ident(&mut _s, "clap");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "Parser");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&item_name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        ::quote::__private::TokenStream::new());
    ::quote::ToTokens::to_tokens(&into_app, &mut _s);
    ::quote::ToTokens::to_tokens(&subcommand, &mut _s);
    _s
}quote! {
121        #[automatically_derived]
122        impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
123
124        #into_app
125        #subcommand
126    })
127}