1moddef::moddef!(
16 flat (pub) mod {
17 },
18 flat (pub(crate)) mod {
19 state,
20 machine,
21 transition,
22 validators
23 }
24);
25
26use crate::{MachinePath, ensure_machine_loaded_by_name};
27use macro_registry::callsite::current_module_path_opt;
28use proc_macro::TokenStream;
29use proc_macro2::Span;
30use quote::quote_spanned;
31use syn::{Item, ItemImpl, parse_macro_input};
32
33#[proc_macro_attribute]
39pub fn state(_attr: TokenStream, item: TokenStream) -> TokenStream {
40 let input = parse_macro_input!(item as Item);
41 let input = match input {
42 Item::Enum(item_enum) => item_enum,
43 other => return invalid_state_target_error(&other).into(),
44 };
45
46 if let Some(error) = validate_state_enum(&input) {
48 return error.into();
49 }
50
51 let enum_info = match EnumInfo::from_item_enum(&input) {
52 Ok(info) => info,
53 Err(err) => return err.to_compile_error().into(),
54 };
55
56 store_state_enum(&enum_info);
58
59 let expanded = generate_state_impls(&enum_info.module_path);
61
62 TokenStream::from(expanded)
63}
64
65#[proc_macro_attribute]
72pub fn machine(_attr: TokenStream, item: TokenStream) -> TokenStream {
73 let input = parse_macro_input!(item as Item);
74 let input = match input {
75 Item::Struct(item_struct) => item_struct,
76 other => return invalid_machine_target_error(&other).into(),
77 };
78
79 let machine_info = match MachineInfo::from_item_struct(&input) {
80 Ok(info) => info,
81 Err(err) => return err.to_compile_error().into(),
82 };
83
84 if let Some(error) = validate_machine_struct(&input, &machine_info) {
86 return error.into();
87 }
88
89 store_machine_struct(&machine_info);
91
92 let expanded = generate_machine_impls(&machine_info, &input);
94
95 TokenStream::from(expanded)
96}
97
98#[proc_macro_attribute]
104pub fn transition(
105 _attr: proc_macro::TokenStream,
106 item: proc_macro::TokenStream,
107) -> proc_macro::TokenStream {
108 let input = parse_macro_input!(item as ItemImpl);
109
110 let tr_impl = match parse_transition_impl(&input) {
112 Ok(parsed) => parsed,
113 Err(err) => return err.into(),
114 };
115
116 let module_path = match resolved_current_module_path(tr_impl.machine_span, "#[transition]") {
117 Ok(path) => path,
118 Err(err) => return err,
119 };
120
121 let machine_path: MachinePath = module_path.clone().into();
122 let machine_info_owned = ensure_machine_loaded_by_name(&machine_path, &tr_impl.machine_name);
123 let machine_info = match machine_info_owned.as_ref() {
124 Some(info) => info,
125 None => {
126 return missing_transition_machine_error(
127 &tr_impl.machine_name,
128 &module_path,
129 tr_impl.machine_span,
130 )
131 .into();
132 }
133 };
134
135 if let Some(err) = validate_transition_functions(&tr_impl, machine_info) {
136 return err.into();
137 }
138
139 let expanded = generate_transition_impl(&input, &tr_impl, machine_info, &module_path);
141
142 expanded.into()
145}
146
147#[proc_macro_attribute]
154pub fn validators(attr: TokenStream, item: TokenStream) -> TokenStream {
155 let module_path = match resolved_current_module_path(Span::call_site(), "#[validators]") {
156 Ok(path) => path,
157 Err(err) => return err,
158 };
159 parse_validators(attr, item, &module_path)
160}
161
162fn resolved_current_module_path(span: Span, macro_name: &str) -> Result<String, TokenStream> {
163 current_module_path_opt().ok_or_else(|| {
164 let message = format!(
165 "Internal error: could not resolve the module path for `{macro_name}` at this call site."
166 );
167 quote_spanned! { span =>
168 compile_error!(#message);
169 }
170 .into()
171 })
172}