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