1use proc_macro::TokenStream;
12use proc_macro2::TokenStream as TokenStream2;
13use quote::quote;
14use syn::{Data, DeriveInput, Fields, parse_macro_input, spanned::Spanned};
15
16#[proc_macro_derive(FsmState)]
41pub fn derive_fsm_state(input: TokenStream) -> TokenStream {
42 let input = parse_macro_input!(input as DeriveInput);
43 match fsm_state_impl(input) {
44 Ok(ts) => ts.into(),
45 Err(e) => e.to_compile_error().into(),
46 }
47}
48
49fn fsm_state_impl(input: DeriveInput) -> syn::Result<TokenStream2> {
50 let name = &input.ident;
51 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
52
53 let data_enum = match &input.data {
54 Data::Enum(e) => e,
55 _ => {
56 return Err(syn::Error::new(
57 input.ident.span(),
58 "`#[derive(FsmState)]` can only be applied to enums",
59 ));
60 }
61 };
62
63 for variant in &data_enum.variants {
65 match &variant.fields {
66 Fields::Unit => {}
67 _ => {
68 return Err(syn::Error::new(
69 variant.span(),
70 "`#[derive(FsmState)]` only supports unit variants (no fields). \
71 Tuple and struct variants are not supported.",
72 ));
73 }
74 }
75 }
76
77 let as_key_arms = data_enum.variants.iter().map(|v| {
79 let ident = &v.ident;
80 let key = ident.to_string();
81 quote! { #name::#ident => #key }
82 });
83
84 let from_key_arms = data_enum.variants.iter().map(|v| {
86 let ident = &v.ident;
87 let key = ident.to_string();
88 quote! { #key => ::std::option::Option::Some(#name::#ident) }
89 });
90
91 Ok(quote! {
92 #[automatically_derived]
93 impl #impl_generics ::ferogram::fsm::FsmState
94 for #name #ty_generics
95 #where_clause
96 {
97 fn as_key(&self) -> ::std::string::String {
98 match self {
99 #(#as_key_arms),*
100 }
101 .to_string()
102 }
103
104 fn from_key(key: &str) -> ::std::option::Option<Self> {
105 match key {
106 #(#from_key_arms),*
107 _ => ::std::option::Option::None,
108 }
109 }
110 }
111 })
112}