1 // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
2 // Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
3 // Andrew Hobden (@hoverbear) <andrew@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 use crate::{
12 attrs::{Attrs, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
13 dummies,
14 utils::Sp,
15 };
16
17 use proc_macro2::{Span, TokenStream};
18 use proc_macro_error::abort_call_site;
19 use quote::quote;
20 use syn::{
21 punctuated::Punctuated, token::Comma, Attribute, Data, DataEnum, DeriveInput, Fields, Ident,
22 Variant,
23 };
24
derive_arg_enum(input: &DeriveInput) -> TokenStream25 pub fn derive_arg_enum(input: &DeriveInput) -> TokenStream {
26 let ident = &input.ident;
27
28 dummies::arg_enum(ident);
29
30 match input.data {
31 Data::Enum(ref e) => gen_for_enum(ident, &input.attrs, e),
32 _ => abort_call_site!("`#[derive(ArgEnum)]` only supports enums"),
33 }
34 }
35
gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStream36 pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStream {
37 if !e.variants.iter().all(|v| matches!(v.fields, Fields::Unit)) {
38 return quote!();
39 };
40
41 let attrs = Attrs::from_struct(
42 Span::call_site(),
43 attrs,
44 Name::Derived(name.clone()),
45 Sp::call_site(DEFAULT_CASING),
46 Sp::call_site(DEFAULT_ENV_CASING),
47 );
48
49 let lits = lits(&e.variants, &attrs);
50 let arg_values = gen_arg_values(&lits);
51 let to_arg_value = gen_to_arg_value(&lits);
52
53 quote! {
54 #[allow(dead_code, unreachable_code, unused_variables)]
55 #[allow(
56 clippy::style,
57 clippy::complexity,
58 clippy::pedantic,
59 clippy::restriction,
60 clippy::perf,
61 clippy::deprecated,
62 clippy::nursery,
63 clippy::cargo
64 )]
65 #[deny(clippy::correctness)]
66 impl clap::ArgEnum for #name {
67 #arg_values
68 #to_arg_value
69 }
70 }
71 }
72
lits( variants: &Punctuated<Variant, Comma>, parent_attribute: &Attrs, ) -> Vec<(TokenStream, Ident)>73 fn lits(
74 variants: &Punctuated<Variant, Comma>,
75 parent_attribute: &Attrs,
76 ) -> Vec<(TokenStream, Ident)> {
77 variants
78 .iter()
79 .filter_map(|variant| {
80 let attrs = Attrs::from_arg_enum_variant(
81 variant,
82 parent_attribute.casing(),
83 parent_attribute.env_casing(),
84 );
85 if let Kind::Skip(_) = &*attrs.kind() {
86 None
87 } else {
88 let fields = attrs.field_methods();
89 let name = attrs.cased_name();
90 Some((
91 quote! {
92 clap::ArgValue::new(#name)
93 #fields
94 },
95 variant.ident.clone(),
96 ))
97 }
98 })
99 .collect::<Vec<_>>()
100 }
101
gen_arg_values(lits: &[(TokenStream, Ident)]) -> TokenStream102 fn gen_arg_values(lits: &[(TokenStream, Ident)]) -> TokenStream {
103 let lit = lits.iter().map(|l| &l.1).collect::<Vec<_>>();
104
105 quote! {
106 fn value_variants<'a>() -> &'a [Self]{
107 &[#(Self::#lit),*]
108 }
109 }
110 }
111
gen_to_arg_value(lits: &[(TokenStream, Ident)]) -> TokenStream112 fn gen_to_arg_value(lits: &[(TokenStream, Ident)]) -> TokenStream {
113 let (lit, variant): (Vec<TokenStream>, Vec<Ident>) = lits.iter().cloned().unzip();
114
115 quote! {
116 fn to_arg_value<'a>(&self) -> Option<clap::ArgValue<'a>> {
117 match self {
118 #(Self::#variant => Some(#lit),)*
119 _ => None
120 }
121 }
122 }
123 }
124