eshanized_polaris_macros/
lib.rs

1//! Procedural macros for Polaris.
2//!
3//! This crate provides the `#[task]` attribute macro and other procedural macros
4//! for simplifying task definitions.
5
6#![forbid(unsafe_code)]
7
8use proc_macro::TokenStream;
9use quote::quote;
10use syn::{parse_macro_input, ItemFn};
11
12/// Attribute macro for defining Polaris tasks.
13///
14/// This macro transforms a regular async function into a Polaris task,
15/// adding necessary serialization bounds and tracing instrumentation.
16///
17/// # Example
18///
19/// ```rust,ignore
20/// use polaris::prelude::*;
21///
22/// #[polaris::task]
23/// async fn compute_square(x: i32) -> PolarisResult<i32> {
24///     Ok(x * x)
25/// }
26/// ```
27///
28/// # Options
29///
30/// - `name`: Custom task name (default: function name)
31/// - `timeout`: Task timeout in seconds
32/// - `retries`: Maximum retry attempts
33///
34/// # Example with options
35///
36/// ```rust,ignore
37/// #[polaris::task(name = "my_task", timeout = 60, retries = 5)]
38/// async fn my_function(input: String) -> PolarisResult<String> {
39///     Ok(input.to_uppercase())
40/// }
41/// ```
42#[proc_macro_attribute]
43pub fn task(_args: TokenStream, input: TokenStream) -> TokenStream {
44    let input_fn = parse_macro_input!(input as ItemFn);
45
46    let fn_name = &input_fn.sig.ident;
47    let fn_vis = &input_fn.vis;
48    let fn_inputs = &input_fn.sig.inputs;
49    let fn_output = &input_fn.sig.output;
50    let fn_body = &input_fn.block;
51    let fn_attrs = &input_fn.attrs;
52
53    // For now, use function name as task name
54    // In a full implementation, we would parse _args to extract options
55    let task_name = fn_name.to_string();
56
57    // Generate the transformed function
58    let expanded = quote! {
59        #(#fn_attrs)*
60        #fn_vis async fn #fn_name(#fn_inputs) #fn_output {
61            // Create tracing span
62            let _span = tracing::info_span!(
63                "task",
64                task_name = #task_name,
65                function = stringify!(#fn_name)
66            ).entered();
67
68            tracing::debug!("Task started");
69
70            // Execute the original function body
71            let result = async {
72                #fn_body
73            }.await;
74
75            match &result {
76                Ok(_) => tracing::debug!("Task completed successfully"),
77                Err(e) => tracing::error!(error = %e, "Task failed"),
78            }
79
80            result
81        }
82    };
83
84    TokenStream::from(expanded)
85}
86
87/// Attribute macro for defining Polaris jobs (collections of tasks).
88///
89/// # Example
90///
91/// ```rust,ignore
92/// #[polaris::job]
93/// async fn batch_process(inputs: Vec<String>) -> PolarisResult<Vec<String>> {
94///     // Process inputs in parallel
95///     Ok(inputs)
96/// }
97/// ```
98#[proc_macro_attribute]
99pub fn job(_args: TokenStream, input: TokenStream) -> TokenStream {
100    let input_fn = parse_macro_input!(input as ItemFn);
101
102    let fn_name = &input_fn.sig.ident;
103    let fn_vis = &input_fn.vis;
104    let fn_inputs = &input_fn.sig.inputs;
105    let fn_output = &input_fn.sig.output;
106    let fn_body = &input_fn.block;
107    let fn_attrs = &input_fn.attrs;
108
109    let expanded = quote! {
110        #(#fn_attrs)*
111        #fn_vis async fn #fn_name(#fn_inputs) #fn_output {
112            let _span = tracing::info_span!(
113                "job",
114                job_name = stringify!(#fn_name)
115            ).entered();
116
117            tracing::info!("Job started");
118
119            let result = async {
120                #fn_body
121            }.await;
122
123            match &result {
124                Ok(_) => tracing::info!("Job completed successfully"),
125                Err(e) => tracing::error!(error = %e, "Job failed"),
126            }
127
128            result
129        }
130    };
131
132    TokenStream::from(expanded)
133}
134
135#[cfg(test)]
136mod tests {
137    // Macro tests are typically done as integration tests
138    // in the tests/ directory or in crates that use the macros
139}