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