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#![doc(html_logo_url = "https://raw.githubusercontent.com/clap-rs/clap/master/assets/clap.png")] 16#![doc = include_str!("../README.md")] 17#![forbid(unsafe_code)] 18 19extern crate proc_macro; 20 21use proc_macro::TokenStream; 22use syn::{parse_macro_input, DeriveInput}; 23use syn::{Data, DataStruct, Fields}; 24 25#[macro_use] 26mod macros; 27 28mod attr; 29mod derives; 30mod dummies; 31mod item; 32mod utils; 33 34/// Generates the `ValueEnum` impl. 35#[proc_macro_derive(ValueEnum, attributes(clap, value))] 36pub fn value_enum(input: TokenStream) -> TokenStream { 37 let input: DeriveInput = parse_macro_input!(input); 38 derives::derive_value_enum(&input) 39 .unwrap_or_else(|err| { 40 let dummy = dummies::value_enum(&input.ident); 41 to_compile_error(err, dummy) 42 }) 43 .into() 44} 45 46/// Generates the `Parser` implementation. 47/// 48/// This is far less verbose than defining the `clap::Command` struct manually, 49/// receiving an instance of `clap::ArgMatches` from conducting parsing, and then 50/// implementing a conversion code to instantiate an instance of the user 51/// context struct. 52#[proc_macro_derive(Parser, attributes(clap, structopt, command, arg, group))] 53pub fn parser(input: TokenStream) -> TokenStream { 54 let input: DeriveInput = parse_macro_input!(input); 55 derives::derive_parser(&input) 56 .unwrap_or_else(|err| { 57 let specific_dummy = match input.data { 58 Data::Struct(DataStruct { 59 fields: Fields::Named(ref _fields), 60 .. 61 }) => Some(dummies::args(&input.ident)), 62 Data::Struct(DataStruct { 63 fields: Fields::Unit, 64 .. 65 }) => Some(dummies::args(&input.ident)), 66 Data::Enum(_) => Some(dummies::subcommand(&input.ident)), 67 _ => None, 68 }; 69 let dummy = specific_dummy 70 .map(|specific_dummy| { 71 let parser_dummy = dummies::parser(&input.ident); 72 quote::quote! { 73 #parser_dummy 74 #specific_dummy 75 } 76 }) 77 .unwrap_or_else(|| quote::quote!()); 78 to_compile_error(err, dummy) 79 }) 80 .into() 81} 82 83/// Generates the `Subcommand` impl. 84#[proc_macro_derive(Subcommand, attributes(clap, command, arg, group))] 85pub fn subcommand(input: TokenStream) -> TokenStream { 86 let input: DeriveInput = parse_macro_input!(input); 87 derives::derive_subcommand(&input) 88 .unwrap_or_else(|err| { 89 let dummy = dummies::subcommand(&input.ident); 90 to_compile_error(err, dummy) 91 }) 92 .into() 93} 94 95/// Generates the `Args` impl. 96#[proc_macro_derive(Args, attributes(clap, command, arg, group))] 97pub fn args(input: TokenStream) -> TokenStream { 98 let input: DeriveInput = parse_macro_input!(input); 99 derives::derive_args(&input) 100 .unwrap_or_else(|err| { 101 let dummy = dummies::args(&input.ident); 102 to_compile_error(err, dummy) 103 }) 104 .into() 105} 106 107fn to_compile_error( 108 error: syn::Error, 109 dummy: proc_macro2::TokenStream, 110) -> proc_macro2::TokenStream { 111 let compile_errors = error.to_compile_error(); 112 quote::quote!( 113 #dummy 114 #compile_errors 115 ) 116} 117