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 = lit_int
33 .base10_parse::<i8>()
34 .expect("value must be i8");
35 }
36 _ => panic!("value must be i8"),
37 },
38 _ => panic!("value must be i8"),
39 }
40 } else {
41 discriminant += 1;
42 }
43 (v.ident.to_string(), discriminant)
44 })
45 .collect(),
46 syn::Data::Struct(_struct) => panic!("structs not supported by ScriptEnum"),
47 syn::Data::Union(_union) => panic!("unions not supported by ScriptEnum"),
48 };
49 let variant_keys: Vec<String> = variants.iter().map(|v| v.0.clone()).collect();
50 let variant_discriminants: Vec<i8> = variants.iter().map(|v| v.1).collect();
51
52 let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl();
54
55 let derived: TokenStream = "#[automatically_derived]"
56 .parse()
57 .expect("derive(ScriptEnum) - derived");
58 let diagnostic: TokenStream = "#[diagnostic::do_not_recommend]"
59 .parse()
60 .expect("derive(ScriptEnum) - diagnostic");
61
62 quote! {
63 #derived
64 #diagnostic
65 impl #impl_generics tinyscript::ScriptEnum for #ident #type_generics #where_clause {
66 fn key_value_tuples<'a>() -> alloc::vec::Vec<(&'a str, i8)> {
67 vec![#((#variant_keys, #variant_discriminants)),*]
68 }
69 }
70 }
71}
72
73#[proc_macro_derive(ScriptEnum, attributes(tscript))]
113pub fn derive_script_enum(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
114 let input: DeriveInput = syn::parse(input).expect("could not parse input");
116
117 derive_scripting_enum(&input).into()
118}