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, punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields, 21 Generics, 22}; 23 24use crate::derives::{args, into_app, subcommand}; 25use crate::item::Item; 26use crate::item::Name; 27 28pub fn derive_parser(input: &DeriveInput) -> Result<TokenStream, syn::Error> { 29 let ident = &input.ident; 30 let pkg_name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default(); 31 32 match input.data { 33 Data::Struct(DataStruct { 34 fields: Fields::Named(ref fields), 35 .. 36 }) => { 37 let name = Name::Assigned(quote!(#pkg_name)); 38 let item = Item::from_args_struct(input, name)?; 39 let fields = fields 40 .named 41 .iter() 42 .map(|field| { 43 let item = Item::from_args_field(field, item.casing(), item.env_casing())?; 44 Ok((field, item)) 45 }) 46 .collect::<Result<Vec<_>, syn::Error>>()?; 47 gen_for_struct(&item, ident, &input.generics, &fields) 48 } 49 Data::Struct(DataStruct { 50 fields: Fields::Unit, 51 .. 52 }) => { 53 let name = Name::Assigned(quote!(#pkg_name)); 54 let item = Item::from_args_struct(input, name)?; 55 let fields = Punctuated::<Field, Comma>::new(); 56 let fields = fields 57 .iter() 58 .map(|field| { 59 let item = Item::from_args_field(field, item.casing(), item.env_casing())?; 60 Ok((field, item)) 61 }) 62 .collect::<Result<Vec<_>, syn::Error>>()?; 63 gen_for_struct(&item, ident, &input.generics, &fields) 64 } 65 Data::Enum(ref e) => { 66 let name = Name::Assigned(quote!(#pkg_name)); 67 let item = Item::from_subcommand_enum(input, name)?; 68 let variants = e 69 .variants 70 .iter() 71 .map(|variant| { 72 let item = 73 Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?; 74 Ok((variant, item)) 75 }) 76 .collect::<Result<Vec<_>, syn::Error>>()?; 77 gen_for_enum(&item, ident, &input.generics, &variants) 78 } 79 _ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"), 80 } 81} 82 83fn gen_for_struct( 84 item: &Item, 85 item_name: &Ident, 86 generics: &Generics, 87 fields: &[(&Field, Item)], 88) -> Result<TokenStream, syn::Error> { 89 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 90 91 let into_app = into_app::gen_for_struct(item, item_name, generics)?; 92 let args = args::gen_for_struct(item, item_name, generics, fields)?; 93 94 Ok(quote! { 95 impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {} 96 97 #into_app 98 #args 99 }) 100} 101 102fn gen_for_enum( 103 item: &Item, 104 item_name: &Ident, 105 generics: &Generics, 106 variants: &[(&Variant, Item)], 107) -> Result<TokenStream, syn::Error> { 108 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 109 110 let into_app = into_app::gen_for_enum(item, item_name, generics)?; 111 let subcommand = subcommand::gen_for_enum(item, item_name, generics, variants)?; 112 113 Ok(quote! { 114 impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {} 115 116 #into_app 117 #subcommand 118 }) 119} 120