clap_derive/
lib.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
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
23use proc_macro::TokenStream;
24use syn::{parse_macro_input, DeriveInput};
25use syn::{Data, DataStruct, Fields};
26
27#[macro_use]
28mod macros;
29
30mod attr;
31mod derives;
32mod dummies;
33mod item;
34mod utils;
35
36/// Generates the `ValueEnum` impl.
37#[proc_macro_derive(ValueEnum, attributes(clap, value))]
38pub fn value_enum(input: TokenStream) -> TokenStream {
39    let input: DeriveInput = parse_macro_input!(input);
40    derives::derive_value_enum(&input)
41        .unwrap_or_else(|err| {
42            let dummy = dummies::value_enum(&input.ident);
43            to_compile_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))]
55pub 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))]
87pub fn subcommand(input: TokenStream) -> TokenStream {
88    let input: DeriveInput = parse_macro_input!(input);
89    derives::derive_subcommand(&input)
90        .unwrap_or_else(|err| {
91            let dummy = dummies::subcommand(&input.ident);
92            to_compile_error(err, dummy)
93        })
94        .into()
95}
96
97/// Generates the `Args` impl.
98#[proc_macro_derive(Args, attributes(clap, command, arg, group))]
99pub fn args(input: TokenStream) -> TokenStream {
100    let input: DeriveInput = parse_macro_input!(input);
101    derives::derive_args(&input)
102        .unwrap_or_else(|err| {
103            let dummy = dummies::args(&input.ident);
104            to_compile_error(err, dummy)
105        })
106        .into()
107}
108
109fn to_compile_error(
110    error: syn::Error,
111    dummy: proc_macro2::TokenStream,
112) -> proc_macro2::TokenStream {
113    let compile_errors = error.to_compile_error();
114    quote::quote!(
115        #dummy
116        #compile_errors
117    )
118}