enum_values_derive/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use proc_macro::TokenStream;
4use syn::{Data, DeriveInput, Expr, ExprLit, Lit, parse_macro_input};
5
6use quote::quote;
7
8/// Generates implementation for the `enum_values::EnumValues` trait on an enum.
9#[proc_macro_derive(EnumValues)]
10pub fn generate_enum_values_impl(input: TokenStream) -> TokenStream {
11    let input = parse_macro_input!(input as DeriveInput);
12    let name = &input.ident;
13
14    let Data::Enum(data_enum) = &input.data else {
15        return syn::Error::new_spanned(name, "EnumReflect only works on enums")
16            .to_compile_error()
17            .into();
18    };
19
20    let get_fields_arms = data_enum
21        .variants
22        .iter()
23        .map(|v| {
24            let variant_ident = &v.ident;
25            let doc = v
26                .attrs
27                .iter()
28                .filter(|a| a.path().is_ident("doc"))
29                .filter_map(|a| {
30                    let expr = if let Ok(nv) = &a.meta.require_name_value() {
31                        &nv.value
32                    } else {
33                        return None;
34                    };
35                    if let Expr::Lit(ExprLit { lit, .. }) = expr
36                        && let Lit::Str(str) = lit
37                    {
38                        return Some(str.value().clone());
39                    }
40                    None
41                })
42                .collect::<String>();
43
44            let Some(discriminant) = v.discriminant.as_ref() else {
45                return syn::Error::new_spanned(name, "No discriminant for enum variant")
46                    .to_compile_error();
47            };
48
49            let value = if let Expr::Lit(lit) = &discriminant.1 {
50                if let Lit::Int(int) = &lit.lit {
51                    if let Ok(value) = int.base10_parse::<u16>() {
52                        value
53                    } else {
54                        return syn::Error::new_spanned(name, "Cannot parse int literal as u16")
55                            .to_compile_error();
56                    }
57                } else {
58                    return syn::Error::new_spanned(name, "Cannot parse int literal")
59                        .to_compile_error();
60                }
61            } else {
62                return syn::Error::new_spanned(name, "Enum discriminant should be a literal")
63                    .to_compile_error();
64            };
65
66            quote! { enum_values::VariantInfo {
67                name: stringify!(#variant_ident),
68                value: #value,
69                doc: #doc.trim()
70            } }
71        })
72        .collect::<Vec<_>>();
73
74    quote! {
75        impl enum_values::EnumValues for #name {
76            fn variants() -> impl Iterator<Item = enum_values::VariantInfo> {
77                [ #(#get_fields_arms),* ].into_iter()
78            }
79        }
80    }
81    .into()
82}