1#[cfg(doctest)]
17#[doc = include_str!("../README.md")]
18mod readme_doctests {}
19
20mod syntax;
21
22moddef::moddef!(
23 flat (pub) mod {
24 },
25 flat (pub(crate)) mod {
26 machine_ref,
27 relation,
28 presentation,
29 state,
30 machine,
31 transition,
32 validators
33 }
34);
35
36pub(crate) use presentation::{
37 PresentationAttr, PresentationTypesAttr, parse_doc_attrs, parse_present_attrs,
38 parse_presentation_types_attr, strip_present_attrs,
39};
40pub(crate) use syntax::{
41 ItemTarget, ModulePath, SourceFingerprint, crate_root_for_file, current_crate_root,
42 extract_derives, source_file_fingerprint,
43};
44
45use macro_registry::callsite::module_path_for_span;
46use proc_macro::TokenStream;
47use proc_macro2::Span;
48use syn::{Item, ItemImpl, parse_macro_input};
49
50#[proc_macro_attribute]
57pub fn state(_attr: TokenStream, item: TokenStream) -> TokenStream {
58 let input = parse_macro_input!(item as Item);
59 let input = match input {
60 Item::Enum(item_enum) => item_enum,
61 other => return invalid_state_target_error(&other).into(),
62 };
63
64 if let Some(error) = validate_state_enum(&input) {
66 return error.into();
67 }
68
69 let enum_info = match EnumInfo::from_item_enum(&input) {
70 Ok(info) => info,
71 Err(err) => return err.to_compile_error().into(),
72 };
73
74 store_state_enum(&enum_info);
76
77 let expanded = generate_state_impls(&enum_info);
79
80 TokenStream::from(expanded)
81}
82
83#[proc_macro_attribute]
92pub fn machine(attr: TokenStream, item: TokenStream) -> TokenStream {
93 let input = parse_macro_input!(item as Item);
94 let input = match input {
95 Item::Struct(item_struct) => item_struct,
96 other => return invalid_machine_target_error(&other).into(),
97 };
98 let role = match parse_machine_attr(attr) {
99 Ok(role) => role,
100 Err(err) => return err.to_compile_error().into(),
101 };
102
103 let machine_info = match MachineInfo::from_item_struct(&input, role) {
104 Ok(info) => info,
105 Err(err) => return err.to_compile_error().into(),
106 };
107
108 if let Some(error) = validate_machine_struct(&input, &machine_info) {
110 return error.into();
111 }
112
113 store_machine_struct(&machine_info);
115
116 let expanded = generate_machine_impls(&machine_info, &input);
118
119 TokenStream::from(expanded)
120}
121
122#[proc_macro_attribute]
128pub fn machine_ref(attr: TokenStream, item: TokenStream) -> TokenStream {
129 machine_ref::parse_machine_ref(attr, item)
130}
131
132#[proc_macro_attribute]
140pub fn transition(
141 attr: proc_macro::TokenStream,
142 item: proc_macro::TokenStream,
143) -> proc_macro::TokenStream {
144 let input = parse_macro_input!(item as ItemImpl);
145 if !attr.is_empty() {
146 let message = "Error: `#[transition]` no longer accepts a machine argument.\nFix: write `#[transition]` on an inherent `impl Machine<State>` block and let Statum infer the machine from the impl target.";
147 return quote::quote_spanned! { input.impl_token.span =>
148 compile_error!(#message);
149 }
150 .into();
151 }
152 let module_path = match resolved_current_module_path(input.impl_token.span, "#[transition]") {
153 Ok(path) => path,
154 Err(err) => return err,
155 };
156
157 let tr_impl = match parse_transition_impl(&input, &module_path) {
159 Ok(parsed) => parsed,
160 Err(err) => return err.into(),
161 };
162
163 if let Some(err) = validate_transition_functions(&tr_impl) {
164 return err.into();
165 }
166
167 let expanded = generate_transition_impl(&input, &tr_impl);
169
170 expanded.into()
173}
174
175#[proc_macro_attribute]
185pub fn validators(attr: TokenStream, item: TokenStream) -> TokenStream {
186 let item_impl = parse_macro_input!(item as ItemImpl);
187 let line_number = item_impl.impl_token.span.start().line;
188 let module_path = match resolved_current_module_path(item_impl.impl_token.span, "#[validators]")
189 {
190 Ok(path) => path,
191 Err(err) => return err,
192 };
193 parse_validators(attr, item_impl, &module_path, line_number)
194}
195
196#[doc(hidden)]
197#[proc_macro]
198pub fn __statum_emit_validator_methods_impl(input: TokenStream) -> TokenStream {
199 validators::emit_validator_methods_impl(input)
200}
201
202pub(crate) fn resolved_current_module_path(
203 span: Span,
204 macro_name: &str,
205) -> Result<String, TokenStream> {
206 let resolved = module_path_for_span(span);
207
208 resolved.ok_or_else(|| {
209 let message = format!(
210 "Internal error: could not resolve the module path for `{macro_name}` at this call site."
211 );
212 quote::quote_spanned! { span =>
213 compile_error!(#message);
214 }
215 .into()
216 })
217}