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 syn::{Item, ItemImpl, parse_macro_input};
31
32#[proc_macro_attribute]
38pub fn state(_attr: TokenStream, item: TokenStream) -> TokenStream {
39 let input = parse_macro_input!(item as Item);
40 let input = match input {
41 Item::Enum(item_enum) => item_enum,
42 other => return invalid_state_target_error(&other).into(),
43 };
44
45 if let Some(error) = validate_state_enum(&input) {
47 return error.into();
48 }
49
50 let enum_info = match EnumInfo::from_item_enum(&input) {
51 Ok(info) => info,
52 Err(err) => return err.to_compile_error().into(),
53 };
54
55 store_state_enum(&enum_info);
57
58 let expanded = generate_state_impls(&enum_info.module_path);
60
61 TokenStream::from(expanded)
62}
63
64#[proc_macro_attribute]
71pub fn machine(_attr: TokenStream, item: TokenStream) -> TokenStream {
72 let input = parse_macro_input!(item as Item);
73 let input = match input {
74 Item::Struct(item_struct) => item_struct,
75 other => return invalid_machine_target_error(&other).into(),
76 };
77
78 let machine_info = match MachineInfo::from_item_struct(&input) {
79 Ok(info) => info,
80 Err(err) => return err.to_compile_error().into(),
81 };
82
83 if let Some(error) = validate_machine_struct(&input, &machine_info) {
85 return error.into();
86 }
87
88 store_machine_struct(&machine_info);
90
91 let expanded = generate_machine_impls(&machine_info, &input);
93
94 TokenStream::from(expanded)
95}
96
97#[proc_macro_attribute]
103pub fn transition(
104 _attr: proc_macro::TokenStream,
105 item: proc_macro::TokenStream,
106) -> proc_macro::TokenStream {
107 let input = parse_macro_input!(item as ItemImpl);
108
109 let tr_impl = match parse_transition_impl(&input) {
111 Ok(parsed) => parsed,
112 Err(err) => return err.into(),
113 };
114
115 let module_path = match resolved_current_module_path(tr_impl.machine_span, "#[transition]") {
116 Ok(path) => path,
117 Err(err) => return err,
118 };
119
120 let machine_path: MachinePath = module_path.clone().into();
121 let machine_info_owned = ensure_machine_loaded_by_name(&machine_path, &tr_impl.machine_name);
122 let machine_info = match machine_info_owned.as_ref() {
123 Some(info) => info,
124 None => {
125 return missing_transition_machine_error(
126 &tr_impl.machine_name,
127 &module_path,
128 tr_impl.machine_span,
129 )
130 .into();
131 }
132 };
133
134 if let Some(err) = validate_transition_functions(&tr_impl, machine_info) {
135 return err.into();
136 }
137
138 let expanded = generate_transition_impl(&input, &tr_impl, machine_info, &module_path);
140
141 expanded.into()
144}
145
146#[proc_macro_attribute]
153pub fn validators(attr: TokenStream, item: TokenStream) -> TokenStream {
154 let module_path = match resolved_current_module_path(Span::call_site(), "#[validators]") {
155 Ok(path) => path,
156 Err(err) => return err,
157 };
158 parse_validators(attr, item, &module_path)
159}
160
161fn resolved_current_module_path(span: Span, macro_name: &str) -> Result<String, TokenStream> {
162 current_module_path_opt().ok_or_else(|| {
163 let message = format!(
164 "Internal error: could not resolve the module path for `{macro_name}` at this call site."
165 );
166 quote::quote_spanned! { span =>
167 compile_error!(#message);
168 }
169 .into()
170 })
171}