Skip to main content

alien_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, Expr, ItemFn};
4
5/// A procedural macro that wraps a function with an AlienEvent scope.
6///
7/// This macro is similar to tracing's `#[instrument]` but for the Alien events system.
8/// It automatically wraps the function body with `AlienEvent::in_scope()`.
9///
10/// # Usage
11///
12/// ```rust
13/// use alien_macros::alien_event;
14/// use alien_core::{AlienEvent, Result};
15///
16/// #[alien_event(AlienEvent::BuildingStack { stack: "my-stack".to_string() })]
17/// async fn build_stack() -> Result<()> {
18///     // Function body - all events emitted here will be children
19///     // of the BuildingStack event
20///     Ok(())
21/// }
22/// ```
23///
24/// The macro supports any AlienEvent variant:
25///
26/// ```rust
27/// #[alien_event(AlienEvent::BuildingImage { image: "api:latest".to_string() })]
28/// async fn build_image() -> Result<()> {
29///     Ok(())
30/// }
31/// ```
32///
33/// You can also use expressions for dynamic values:
34///
35/// ```rust
36/// #[alien_event(AlienEvent::BuildingStack { stack: format!("stack-{}", id) })]
37/// async fn build_dynamic_stack(id: u32) -> Result<()> {
38///     Ok(())
39/// }
40/// ```
41///
42/// The macro only works with async functions since it uses `AlienEvent::in_scope()` internally.
43#[proc_macro_attribute]
44pub fn alien_event(args: TokenStream, input: TokenStream) -> TokenStream {
45    let event_expr = parse_macro_input!(args as Expr);
46    let input_fn = parse_macro_input!(input as ItemFn);
47
48    let fn_vis = &input_fn.vis;
49    let fn_sig = &input_fn.sig;
50    let fn_block = &input_fn.block;
51    let fn_attrs = &input_fn.attrs;
52
53    // Check if function is async
54    let is_async = fn_sig.asyncness.is_some();
55
56    if is_async {
57        // For async functions, use in_scope for automatic success/failure tracking
58        let expanded = quote! {
59            #(#fn_attrs)*
60            #fn_vis #fn_sig {
61                (#event_expr).in_scope(|_event_handle| async move #fn_block).await
62            }
63        };
64        TokenStream::from(expanded)
65    } else {
66        // For sync functions, we can't use in_scope since it's async
67        // Instead, we'll just note that this macro is designed for async functions
68        panic!("alien_event macro currently only supports async functions. Use AlienEvent::emit() manually for sync functions.");
69    }
70}
71
72mod controller;
73
74use controller::{controller_impl, controller_struct};
75
76/// Macro for simplifying resource controller implementations.
77///
78/// When applied to a struct, it adds internal state management fields.
79/// When applied to an impl block, it generates the ResourceController trait implementation.
80///
81/// # Usage on struct:
82/// ```rust
83/// #[controller]
84/// struct AwsFunctionController {
85///     pub arn: Option<String>,
86///     pub url: Option<String>,
87/// }
88/// ```
89///
90/// # Usage on impl:
91/// ```rust
92/// #[controller]
93/// impl AwsFunctionController {
94///     #[flow_entry(Create)]
95///     #[handler(state = CreateStart, on_failure = CreateFailed, status = ResourceStatus::Provisioning)]
96///     async fn create_start(&mut self, ctx: &ResourceControllerContext) -> Result<HandlerAction> {
97///         // ...
98///     }
99/// }
100/// ```
101#[proc_macro_attribute]
102pub fn controller(args: TokenStream, input: TokenStream) -> TokenStream {
103    // Check if this is a struct or impl block
104    if syn::parse::<syn::ItemStruct>(input.clone()).is_ok() {
105        return controller_struct(args, input);
106    }
107
108    if syn::parse::<syn::ItemImpl>(input.clone()).is_ok() {
109        return controller_impl(args, input);
110    }
111
112    panic!("controller macro can only be applied to structs or impl blocks");
113}
114
115/// Helper attributes for the controller macro
116#[proc_macro_attribute]
117pub fn flow_entry(_args: TokenStream, input: TokenStream) -> TokenStream {
118    // This is just a marker attribute, return input unchanged
119    input
120}
121
122#[proc_macro_attribute]
123pub fn handler(_args: TokenStream, input: TokenStream) -> TokenStream {
124    // This is just a marker attribute, return input unchanged
125    input
126}
127
128/// Macro for defining terminal states in a controller
129#[proc_macro]
130pub fn terminal_state(_input: TokenStream) -> TokenStream {
131    // This will be used inside the controller impl
132    // For now, just return an empty token stream as it will be handled by the controller macro
133    quote::quote! {}.into()
134}