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