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