1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::{
attrs::{Attrs, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
dummies,
utils::Sp,
};
use proc_macro2::{Span, TokenStream};
use proc_macro_error::{abort, abort_call_site};
use quote::quote;
use quote::quote_spanned;
use syn::{
punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, DataEnum, DeriveInput,
Fields, Ident, Variant,
};
pub fn derive_value_enum(input: &DeriveInput) -> TokenStream {
let ident = &input.ident;
dummies::value_enum(ident);
match input.data {
Data::Enum(ref e) => gen_for_enum(ident, &input.attrs, e),
_ => abort_call_site!("`#[derive(ValueEnum)]` only supports enums"),
}
}
pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStream {
let attrs = Attrs::from_struct(
Span::call_site(),
attrs,
Name::Derived(name.clone()),
Sp::call_site(DEFAULT_CASING),
Sp::call_site(DEFAULT_ENV_CASING),
);
let lits = lits(&e.variants, &attrs);
let value_variants = gen_value_variants(&lits);
let to_possible_value = gen_to_possible_value(&lits);
quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
#[allow(
clippy::style,
clippy::complexity,
clippy::pedantic,
clippy::restriction,
clippy::perf,
clippy::deprecated,
clippy::nursery,
clippy::cargo,
clippy::suspicious_else_formatting,
)]
#[deny(clippy::correctness)]
impl clap::ValueEnum for #name {
#value_variants
#to_possible_value
}
}
}
fn lits(
variants: &Punctuated<Variant, Comma>,
parent_attribute: &Attrs,
) -> Vec<(TokenStream, Ident)> {
variants
.iter()
.filter_map(|variant| {
let attrs = Attrs::from_value_enum_variant(
variant,
parent_attribute.casing(),
parent_attribute.env_casing(),
);
if let Kind::Skip(_) = &*attrs.kind() {
None
} else {
if !matches!(variant.fields, Fields::Unit) {
abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped");
}
let fields = attrs.field_methods(false);
let name = attrs.cased_name();
Some((
quote_spanned! { variant.span()=>
clap::PossibleValue::new(#name)
#fields
},
variant.ident.clone(),
))
}
})
.collect::<Vec<_>>()
}
fn gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream {
let lit = lits.iter().map(|l| &l.1).collect::<Vec<_>>();
quote! {
fn value_variants<'a>() -> &'a [Self]{
&[#(Self::#lit),*]
}
}
}
fn gen_to_possible_value(lits: &[(TokenStream, Ident)]) -> TokenStream {
let (lit, variant): (Vec<TokenStream>, Vec<Ident>) = lits.iter().cloned().unzip();
quote! {
fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::PossibleValue<'a>> {
match self {
#(Self::#variant => Some(#lit),)*
_ => None
}
}
}
}