• 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 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(dead_code, unreachable_code, unused_variables, unused_braces)]
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             clippy::suspicious_else_formatting,
65             clippy::almost_swapped,
66         )]
67         impl clap::ValueEnum for #item_name {
68             #value_variants
69             #to_possible_value
70         }
71     })
72 }
73 
lits(variants: &[(&Variant, Item)]) -> Result<Vec<(TokenStream, Ident)>, syn::Error>74 fn lits(variants: &[(&Variant, Item)]) -> Result<Vec<(TokenStream, Ident)>, syn::Error> {
75     let mut genned = Vec::new();
76     for (variant, item) in variants {
77         if let Kind::Skip(_, _) = &*item.kind() {
78             continue;
79         }
80         if !matches!(variant.fields, Fields::Unit) {
81             abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped");
82         }
83         let fields = item.field_methods();
84         let deprecations = item.deprecations();
85         let name = item.cased_name();
86         genned.push((
87             quote_spanned! { variant.span()=> {
88                 #deprecations
89                 clap::builder::PossibleValue::new(#name)
90                 #fields
91             }},
92             variant.ident.clone(),
93         ));
94     }
95     Ok(genned)
96 }
97 
gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream98 fn gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream {
99     let lit = lits.iter().map(|l| &l.1).collect::<Vec<_>>();
100 
101     quote! {
102         fn value_variants<'a>() -> &'a [Self]{
103             &[#(Self::#lit),*]
104         }
105     }
106 }
107 
gen_to_possible_value(item: &Item, lits: &[(TokenStream, Ident)]) -> TokenStream108 fn gen_to_possible_value(item: &Item, lits: &[(TokenStream, Ident)]) -> TokenStream {
109     let (lit, variant): (Vec<TokenStream>, Vec<Ident>) = lits.iter().cloned().unzip();
110 
111     let deprecations = item.deprecations();
112 
113     quote! {
114         fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::builder::PossibleValue> {
115             #deprecations
116             match self {
117                 #(Self::#variant => Some(#lit),)*
118                 _ => None
119             }
120         }
121     }
122 }
123