| 1 | // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>, | 
| 2 | // Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and | 
|---|
| 3 | // Ana Hobden (@hoverbear) <operator@hoverbear.org> | 
|---|
| 4 | // | 
|---|
| 5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | 
|---|
| 6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | 
|---|
| 7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | 
|---|
| 8 | // option. This file may not be copied, modified, or distributed | 
|---|
| 9 | // except according to those terms. | 
|---|
| 10 | // | 
|---|
| 11 | // This work was derived from Structopt (https://github.com/TeXitoi/structopt) | 
|---|
| 12 | // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the | 
|---|
| 13 | // MIT/Apache 2.0 license. | 
|---|
| 14 |  | 
|---|
| 15 | use proc_macro2::TokenStream; | 
|---|
| 16 | use quote::quote; | 
|---|
| 17 | use syn::Ident; | 
|---|
| 18 | use syn::Variant; | 
|---|
| 19 | use syn::{ | 
|---|
| 20 | self, punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields, | 
|---|
| 21 | Generics, | 
|---|
| 22 | }; | 
|---|
| 23 |  | 
|---|
| 24 | use crate::derives::args::collect_args_fields; | 
|---|
| 25 | use crate::derives::{args, into_app, subcommand}; | 
|---|
| 26 | use crate::item::Item; | 
|---|
| 27 | use crate::item::Name; | 
|---|
| 28 |  | 
|---|
| 29 | pub(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 | match input.data { | 
|---|
| 34 | Data::Struct(DataStruct { | 
|---|
| 35 | fields: Fields::Named(ref fields), | 
|---|
| 36 | .. | 
|---|
| 37 | }) => { | 
|---|
| 38 | let name = Name::Assigned(quote!(#pkg_name)); | 
|---|
| 39 | let item = Item::from_args_struct(input, name)?; | 
|---|
| 40 | let fields = collect_args_fields(&item, fields)?; | 
|---|
| 41 | gen_for_struct(&item, ident, &input.generics, &fields) | 
|---|
| 42 | } | 
|---|
| 43 | Data::Struct(DataStruct { | 
|---|
| 44 | fields: Fields::Unit, | 
|---|
| 45 | .. | 
|---|
| 46 | }) => { | 
|---|
| 47 | let name = Name::Assigned(quote!(#pkg_name)); | 
|---|
| 48 | let item = Item::from_args_struct(input, name)?; | 
|---|
| 49 | let fields = Punctuated::<Field, Comma>::new(); | 
|---|
| 50 | let fields = fields | 
|---|
| 51 | .iter() | 
|---|
| 52 | .map(|field| { | 
|---|
| 53 | let item = Item::from_args_field(field, item.casing(), item.env_casing())?; | 
|---|
| 54 | Ok((field, item)) | 
|---|
| 55 | }) | 
|---|
| 56 | .collect::<Result<Vec<_>, syn::Error>>()?; | 
|---|
| 57 | gen_for_struct(&item, ident, &input.generics, &fields) | 
|---|
| 58 | } | 
|---|
| 59 | Data::Enum(ref e) => { | 
|---|
| 60 | let name = Name::Assigned(quote!(#pkg_name)); | 
|---|
| 61 | let item = Item::from_subcommand_enum(input, name)?; | 
|---|
| 62 | let variants = e | 
|---|
| 63 | .variants | 
|---|
| 64 | .iter() | 
|---|
| 65 | .map(|variant| { | 
|---|
| 66 | let item = | 
|---|
| 67 | Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?; | 
|---|
| 68 | Ok((variant, item)) | 
|---|
| 69 | }) | 
|---|
| 70 | .collect::<Result<Vec<_>, syn::Error>>()?; | 
|---|
| 71 | gen_for_enum(&item, ident, &input.generics, &variants) | 
|---|
| 72 | } | 
|---|
| 73 | _ => abort_call_site!( "`#[derive(Parser)]` only supports non-tuple structs and enums"), | 
|---|
| 74 | } | 
|---|
| 75 | } | 
|---|
| 76 |  | 
|---|
| 77 | fn gen_for_struct( | 
|---|
| 78 | item: &Item, | 
|---|
| 79 | item_name: &Ident, | 
|---|
| 80 | generics: &Generics, | 
|---|
| 81 | fields: &[(&Field, Item)], | 
|---|
| 82 | ) -> Result<TokenStream, syn::Error> { | 
|---|
| 83 | let (impl_generics: ImplGenerics<'_>, ty_generics: TypeGenerics<'_>, where_clause: Option<&WhereClause>) = generics.split_for_impl(); | 
|---|
| 84 |  | 
|---|
| 85 | let into_app: TokenStream = into_app::gen_for_struct(item, item_name, generics)?; | 
|---|
| 86 | let args: TokenStream = args::gen_for_struct(item, item_name, generics, fields)?; | 
|---|
| 87 |  | 
|---|
| 88 | Ok(quote! { | 
|---|
| 89 | #[automatically_derived] | 
|---|
| 90 | #[allow( | 
|---|
| 91 | unused_qualifications, | 
|---|
| 92 | clippy::redundant_locals, | 
|---|
| 93 | )] | 
|---|
| 94 | impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {} | 
|---|
| 95 |  | 
|---|
| 96 | #into_app | 
|---|
| 97 | #args | 
|---|
| 98 | }) | 
|---|
| 99 | } | 
|---|
| 100 |  | 
|---|
| 101 | fn gen_for_enum( | 
|---|
| 102 | item: &Item, | 
|---|
| 103 | item_name: &Ident, | 
|---|
| 104 | generics: &Generics, | 
|---|
| 105 | variants: &[(&Variant, Item)], | 
|---|
| 106 | ) -> Result<TokenStream, syn::Error> { | 
|---|
| 107 | let (impl_generics: ImplGenerics<'_>, ty_generics: TypeGenerics<'_>, where_clause: Option<&WhereClause>) = generics.split_for_impl(); | 
|---|
| 108 |  | 
|---|
| 109 | let into_app: TokenStream = into_app::gen_for_enum(item, item_name, generics)?; | 
|---|
| 110 | let subcommand: TokenStream = subcommand::gen_for_enum(item, item_name, generics, variants)?; | 
|---|
| 111 |  | 
|---|
| 112 | Ok(quote! { | 
|---|
| 113 | #[automatically_derived] | 
|---|
| 114 | impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {} | 
|---|
| 115 |  | 
|---|
| 116 | #into_app | 
|---|
| 117 | #subcommand | 
|---|
| 118 | }) | 
|---|
| 119 | } | 
|---|
| 120 |  | 
|---|