1 // Copyright 2018 Guillaume Pinot (@TeXitoi) <[email protected]>,
2 // Kevin Knapp (@kbknapp) <[email protected]>, and
3 // Ana Hobden (@hoverbear) <[email protected]>
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 proc_macro2::TokenStream;
12 use quote::quote;
13 use quote::quote_spanned;
14 use syn::{spanned::Spanned, Data, DeriveInput, Fields, Ident, Variant};
15
16 use crate::item::{Item, Kind, Name};
17
derive_value_enum(input: &DeriveInput) -> Result<TokenStream, syn::Error>18 pub fn derive_value_enum(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
19 let ident = &input.ident;
20
21 match input.data {
22 Data::Enum(ref e) => {
23 let name = Name::Derived(ident.clone());
24 let item = Item::from_value_enum(input, name)?;
25 let mut variants = Vec::new();
26 for variant in &e.variants {
27 let item =
28 Item::from_value_enum_variant(variant, item.casing(), item.env_casing())?;
29 variants.push((variant, item));
30 }
31 gen_for_enum(&item, ident, &variants)
32 }
33 _ => abort_call_site!("`#[derive(ValueEnum)]` only supports enums"),
34 }
35 }
36
gen_for_enum( item: &Item, item_name: &Ident, variants: &[(&Variant, Item)], ) -> Result<TokenStream, syn::Error>37 pub fn gen_for_enum(
38 item: &Item,
39 item_name: &Ident,
40 variants: &[(&Variant, Item)],
41 ) -> Result<TokenStream, syn::Error> {
42 if !matches!(&*item.kind(), Kind::Value) {
43 abort! { item.kind().span(),
44 "`{}` cannot be used with `value`",
45 item.kind().name(),
46 }
47 }
48
49 let lits = lits(variants)?;
50 let value_variants = gen_value_variants(&lits);
51 let to_possible_value = gen_to_possible_value(item, &lits);
52
53 Ok(quote! {
54 #[allow(
55 dead_code,
56 unreachable_code,
57 unused_variables,
58 unused_braces,
59 unused_qualifications,
60 )]
61 #[allow(
62 clippy::style,
63 clippy::complexity,
64 clippy::pedantic,
65 clippy::restriction,
66 clippy::perf,
67 clippy::deprecated,
68 clippy::nursery,
69 clippy::cargo,
70 clippy::suspicious_else_formatting,
71 clippy::almost_swapped,
72 clippy::redundant_locals,
73 )]
74 #[automatically_derived]
75 impl clap::ValueEnum for #item_name {
76 #value_variants
77 #to_possible_value
78 }
79 })
80 }
81
lits(variants: &[(&Variant, Item)]) -> Result<Vec<(TokenStream, Ident)>, syn::Error>82 fn lits(variants: &[(&Variant, Item)]) -> Result<Vec<(TokenStream, Ident)>, syn::Error> {
83 let mut genned = Vec::new();
84 for (variant, item) in variants {
85 if let Kind::Skip(_, _) = &*item.kind() {
86 continue;
87 }
88 if !matches!(variant.fields, Fields::Unit) {
89 abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped");
90 }
91 let fields = item.field_methods();
92 let deprecations = item.deprecations();
93 let name = item.cased_name();
94 genned.push((
95 quote_spanned! { variant.span()=> {
96 #deprecations
97 clap::builder::PossibleValue::new(#name)
98 #fields
99 }},
100 variant.ident.clone(),
101 ));
102 }
103 Ok(genned)
104 }
105
gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream106 fn gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream {
107 let lit = lits.iter().map(|l| &l.1).collect::<Vec<_>>();
108
109 quote! {
110 fn value_variants<'a>() -> &'a [Self]{
111 &[#(Self::#lit),*]
112 }
113 }
114 }
115
gen_to_possible_value(item: &Item, lits: &[(TokenStream, Ident)]) -> TokenStream116 fn gen_to_possible_value(item: &Item, lits: &[(TokenStream, Ident)]) -> TokenStream {
117 let (lit, variant): (Vec<TokenStream>, Vec<Ident>) = lits.iter().cloned().unzip();
118
119 let deprecations = item.deprecations();
120
121 quote! {
122 fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::builder::PossibleValue> {
123 #deprecations
124 match self {
125 #(Self::#variant => Some(#lit),)*
126 _ => None
127 }
128 }
129 }
130 }
131