1#[doc(hidden)]
5extern crate proc_macro;
6
7#[doc(hidden)]
8extern crate alloc;
9
10use proc_macro2::TokenStream;
11use quote::quote;
12use syn::{DeriveInput, Expr, Lit};
13
14fn derive_scripting_enum(input: &DeriveInput) -> TokenStream {
16 let ident = &input.ident;
18
19 let mut discriminant = -1_i8;
21 let variants: Vec<(String, i8)> = match &input.data {
22 syn::Data::Enum(data) => data
23 .variants
24 .iter()
25 .map(|v| {
26 if let Some((_eq, expr)) = &v.discriminant {
27 match expr {
28 Expr::Lit(expr_lit) => match &expr_lit.lit {
29 Lit::Int(lit_int) => {
30 discriminant = lit_int
31 .base10_parse::<i8>()
32 .expect("value must be i8");
33 }
34 _ => panic!("value must be i8"),
35 },
36 _ => panic!("value must be i8"),
37 }
38 } else {
39 discriminant += 1;
40 }
41 (v.ident.to_string(), discriminant)
42 })
43 .collect(),
44 syn::Data::Struct(_struct) => panic!("structs not supported by ScriptEnum"),
45 syn::Data::Union(_union) => panic!("unions not supported by ScriptEnum"),
46 };
47 let variant_keys: Vec<String> = variants.iter().map(|v| v.0.clone()).collect();
48 let variant_discriminants: Vec<i8> = variants.iter().map(|v| v.1).collect();
49
50 let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl();
52
53 let derived: TokenStream = "#[automatically_derived]"
54 .parse()
55 .expect("derive(ScriptEnum) - derived");
56 let diagnostic: TokenStream = "#[diagnostic::do_not_recommend]"
57 .parse()
58 .expect("derive(ScriptEnum) - diagnostic");
59
60 quote! {
61 #derived
62 #diagnostic
63 impl #impl_generics tinyscript::ScriptEnum for #ident #type_generics #where_clause {
64 fn key_value_tuples<'a>() -> alloc::vec::Vec<(&'a str, i8)> {
65 vec![#((#variant_keys, #variant_discriminants)),*]
66 }
67 }
68 }
69}
70
71#[proc_macro_derive(ScriptEnum, attributes(tscript))]
111pub fn derive_script_enum(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
112 let input: DeriveInput = syn::parse(input).expect("could not parse input");
114
115 derive_scripting_enum(&input).into()
116}