rsactor_derive/lib.rs
1// Copyright 2022 Jeff Kim <hiking90@gmail.com>
2// SPDX-License-Identifier: Apache-2.0
3
4//! Derive macros for rsActor framework
5//!
6//! This crate provides derive macros for the rsActor framework, allowing users
7//! to automatically implement common traits with sensible defaults.
8//!
9//! ## Actor Derive Macro
10//!
11//! The `#[derive(Actor)]` macro provides a convenient way to implement the Actor trait
12//! for simple structs and enums that don't require complex initialization logic.
13//!
14//! ### Generated Implementation
15//!
16//! When you use `#[derive(Actor)]`, it generates:
17//! - `Args` type set to `Self` (the struct or enum itself)
18//! - `Error` type set to `std::convert::Infallible` (never fails)
19//! - `on_start` method that simply returns the provided args
20//!
21//! ### Usage
22//!
23//! #### With Structs
24//! ```rust
25//! use rsactor::Actor;
26//!
27//! #[derive(Actor)]
28//! struct MyActor {
29//! name: String,
30//! count: u32,
31//! }
32//! ```
33//!
34//! #### With Enums
35//! ```rust
36//! use rsactor::Actor;
37//!
38//! #[derive(Actor)]
39//! enum StateActor {
40//! Idle,
41//! Processing(String),
42//! Completed(i32),
43//! }
44//! ```
45//!
46//! This is equivalent to manually writing:
47//!
48//! ```rust
49//! # struct MyActor { name: String, count: u32 }
50//! use rsactor::{Actor, ActorRef};
51//! use std::convert::Infallible;
52//!
53//! impl Actor for MyActor {
54//! type Args = Self;
55//! type Error = Infallible;
56//!
57//! async fn on_start(
58//! args: Self::Args,
59//! _actor_ref: &ActorRef<Self>,
60//! ) -> std::result::Result<Self, Self::Error> {
61//! Ok(args)
62//! }
63//! }
64//! ```
65//!
66//! ### When to Use
67//!
68//! Use the derive macro when:
69//! - Your actor doesn't need complex initialization
70//! - You want to pass a fully constructed instance to `spawn()`
71//! - You don't need custom error handling during initialization
72//!
73//! For complex initialization (async resource setup, validation, etc.),
74//! implement the Actor trait manually.
75//!
76//! ## Message Handlers Attribute Macro
77//!
78//! The `#[message_handlers]` attribute macro combined with `#[handler]` method attributes
79//! provides an automated way to generate Message trait implementations and register message handlers.
80//!
81//! ### Usage
82//!
83//! ```rust
84//! use rsactor::{Actor, ActorRef, message_handlers};
85//!
86//! #[derive(Actor)]
87//! struct MyActor {
88//! count: u32,
89//! }
90//!
91//! struct Increment;
92//! struct GetCount;
93//!
94//! #[message_handlers]
95//! impl MyActor {
96//! #[handler]
97//! async fn handle_increment(&mut self, _msg: Increment, _: &ActorRef<Self>) -> u32 {
98//! self.count += 1;
99//! self.count
100//! }
101//!
102//! #[handler]
103//! async fn handle_get_count(&mut self, _msg: GetCount, _: &ActorRef<Self>) -> u32 {
104//! self.count
105//! }
106//!
107//! // Regular methods without #[handler] are left unchanged
108//! fn internal_method(&self) -> u32 {
109//! self.count * 2
110//! }
111//! }
112//! ```
113//!
114//! ### Benefits
115//!
116//! - Automatic generation of `Message<T>` trait implementations
117//! - Selective processing: only methods with `#[handler]` attribute are processed
118//! - Reduced boilerplate and potential for errors
119//! - Type-safe message handling with compile-time checks
120
121use proc_macro::TokenStream;
122use proc_macro2::TokenStream as TokenStream2;
123use quote::quote;
124use syn::{
125 parse_macro_input, Data, DeriveInput, FnArg, ImplItem, ImplItemFn, ItemImpl, PatType,
126 ReturnType, Type,
127};
128
129/// Derive macro for automatically implementing the Actor trait.
130///
131/// This macro generates a default implementation of the Actor trait where:
132/// - `Args` type is set to `Self`
133/// - `Error` type is set to `std::convert::Infallible`
134/// - `on_start` method returns the args as the actor instance
135///
136/// # Examples
137///
138/// ## Struct Actor
139/// ```rust
140/// use rsactor::Actor;
141///
142/// #[derive(Actor)]
143/// struct MyActor {
144/// name: String,
145/// }
146/// ```
147///
148/// ## Enum Actor
149/// ```rust
150/// use rsactor::Actor;
151///
152/// #[derive(Actor)]
153/// enum StateActor {
154/// Idle,
155/// Processing(String),
156/// Completed(i32),
157/// }
158/// ```
159///
160/// This generates an implementation equivalent to:
161///
162/// ```rust
163/// # struct MyActor { name: String }
164/// impl rsactor::Actor for MyActor {
165/// type Args = Self;
166/// type Error = std::convert::Infallible;
167///
168/// async fn on_start(
169/// args: Self::Args,
170/// _actor_ref: &rsactor::ActorRef<Self>,
171/// ) -> std::result::Result<Self, Self::Error> {
172/// Ok(args)
173/// }
174/// }
175/// ```
176///
177/// # Limitations
178///
179/// - Only works on structs and enums (not unions)
180/// - Generates a very basic implementation - for complex initialization logic,
181/// implement the Actor trait manually
182#[proc_macro_derive(Actor)]
183pub fn derive_actor(input: TokenStream) -> TokenStream {
184 let input = parse_macro_input!(input as DeriveInput);
185
186 let expanded = match derive_actor_impl(input) {
187 Ok(tokens) => tokens,
188 Err(err) => err.to_compile_error(),
189 };
190
191 TokenStream::from(expanded)
192}
193
194fn derive_actor_impl(input: DeriveInput) -> syn::Result<TokenStream2> {
195 let name = &input.ident;
196 let generics = &input.generics;
197 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
198
199 // Check if it's a struct or enum
200 match &input.data {
201 Data::Struct(_) | Data::Enum(_) => {
202 // Generate the Actor implementation with proper generic support
203 let expanded = quote! {
204 impl #impl_generics rsactor::Actor for #name #ty_generics #where_clause {
205 type Args = Self;
206 type Error = std::convert::Infallible;
207
208 async fn on_start(
209 args: Self::Args,
210 _actor_ref: &rsactor::ActorRef<Self>,
211 ) -> std::result::Result<Self, Self::Error> {
212 Ok(args)
213 }
214 }
215 };
216
217 Ok(expanded)
218 }
219 _ => Err(syn::Error::new_spanned(
220 name,
221 "Actor derive macro can only be used on structs and enums",
222 )),
223 }
224}
225
226/// Attribute macro for automatically generating Message trait implementations
227/// from method definitions.
228///
229/// This macro analyzes method signatures in an impl block and generates the corresponding
230/// Message trait implementations for methods marked with `#[handler]` attribute, reducing
231/// boilerplate code.
232///
233/// # Usage
234///
235/// ```rust
236/// use rsactor::{Actor, ActorRef, message_handlers};
237///
238/// #[derive(Actor)]
239/// struct MyActor {
240/// count: u32,
241/// }
242///
243/// struct Increment;
244/// struct Decrement;
245///
246/// #[message_handlers]
247/// impl MyActor {
248/// #[handler]
249/// async fn handle_increment(&mut self, _msg: Increment, _: &ActorRef<Self>) -> u32 {
250/// self.count += 1;
251/// self.count
252/// }
253///
254/// #[handler]
255/// async fn handle_decrement(&mut self, _msg: Decrement, _: &ActorRef<Self>) -> u32 {
256/// self.count -= 1;
257/// self.count
258/// }
259///
260/// // Regular methods without #[handler] are left unchanged
261/// fn get_internal_state(&self) -> u32 {
262/// self.count
263/// }
264/// }
265/// ```
266///
267/// This will automatically generate:
268/// - The `Message<MessageType>` trait implementations for each handler method
269///
270/// # Requirements
271///
272/// Each method marked with `#[handler]` must follow this signature pattern:
273/// - Must be an `async fn`
274/// - First parameter: `&mut self`
275/// - Second parameter: `msg: MessageType` (where MessageType is the message struct)
276/// - Third parameter: `&ActorRef<Self>` or `&rsactor::ActorRef<Self>`
277/// - Return type: the reply type for the message
278///
279/// # Error Messages
280///
281/// The macro provides detailed error messages for common mistakes:
282/// - Wrong parameter count
283/// - Missing `async` keyword
284/// - Incorrect parameter types
285/// - Invalid #[handler] attribute usage
286///
287/// # Benefits
288///
289/// - No need to manually implement `Message<T>` trait for each message type
290/// - Reduces boilerplate code and potential for errors
291/// - Only processes methods marked with `#[handler]`, leaving other methods unchanged
292#[proc_macro_attribute]
293pub fn message_handlers(_attr: TokenStream, item: TokenStream) -> TokenStream {
294 let input = parse_macro_input!(item as ItemImpl);
295
296 let expanded = match message_impl(input) {
297 Ok(tokens) => tokens,
298 Err(err) => err.to_compile_error(),
299 };
300
301 TokenStream::from(expanded)
302}
303
304fn message_impl(mut input: ItemImpl) -> syn::Result<TokenStream2> {
305 let actor_type = &input.self_ty;
306 let generics = &input.generics;
307
308 let message_impls = process_handler_methods(&input.items, actor_type, generics)?;
309
310 // Remove `#[handler]` attributes from the impl block for clean output
311 clean_handler_attributes(&mut input.items);
312
313 let result = quote! {
314 #input
315 #(#message_impls)*
316 };
317
318 Ok(result)
319}
320
321fn process_handler_methods(
322 items: &[ImplItem],
323 actor_type: &Type,
324 generics: &syn::Generics,
325) -> syn::Result<Vec<TokenStream2>> {
326 let mut message_impls = Vec::new();
327
328 for item in items {
329 if let ImplItem::Fn(method) = item {
330 // Check for `#[handler]` attribute with better error handling
331 let handler_attr = method
332 .attrs
333 .iter()
334 .find(|attr| attr.path().is_ident("handler"));
335
336 if let Some(attr) = handler_attr {
337 // Validate attribute syntax - check if it's a simple path attribute
338 match &attr.meta {
339 syn::Meta::Path(_) => {
340 // This is correct: #[handler] with no arguments
341 }
342 _ => {
343 return Err(syn::Error::new_spanned(
344 attr,
345 "The #[handler] attribute does not accept any arguments",
346 ));
347 }
348 }
349
350 // Generate message implementation for this method
351 let impl_tokens = generate_message_impl(method, actor_type, generics)?;
352 message_impls.push(impl_tokens);
353 }
354 }
355 }
356
357 Ok(message_impls)
358}
359
360fn clean_handler_attributes(items: &mut [ImplItem]) {
361 for item in items {
362 if let ImplItem::Fn(method) = item {
363 method.attrs.retain(|attr| !attr.path().is_ident("handler"));
364 }
365 }
366}
367
368fn generate_message_impl(
369 method: &ImplItemFn,
370 actor_type: &Type,
371 generics: &syn::Generics,
372) -> syn::Result<TokenStream2> {
373 // Parse method signature
374 let inputs = &method.sig.inputs;
375
376 // Validate that the method is async
377 if method.sig.asyncness.is_none() {
378 return Err(syn::Error::new_spanned(
379 &method.sig,
380 format!("Handler method '{}' must be async", method.sig.ident),
381 ));
382 }
383
384 if inputs.len() != 3 {
385 return Err(syn::Error::new_spanned(
386 &method.sig,
387 format!(
388 "Message handler method '{}' must have exactly 3 parameters: &mut self, message, &ActorRef<Self>. Found {} parameters.",
389 method.sig.ident,
390 inputs.len()
391 )
392 ));
393 }
394
395 // Validate first parameter (&mut self)
396 if !matches!(&inputs[0], FnArg::Receiver(receiver) if receiver.mutability.is_some()) {
397 return Err(syn::Error::new_spanned(
398 &inputs[0],
399 "First parameter must be '&mut self'",
400 ));
401 }
402
403 // Extract message type from second parameter
404 let message_type = match &inputs[1] {
405 FnArg::Typed(PatType { ty, .. }) => ty,
406 _ => {
407 return Err(syn::Error::new_spanned(
408 &inputs[1],
409 "Second parameter must be a typed message parameter (e.g., 'msg: MessageType')",
410 ))
411 }
412 };
413
414 // Validate third parameter (&ActorRef<Self>)
415 let third_param_valid = match &inputs[2] {
416 FnArg::Typed(PatType { ty, .. }) => {
417 // Check if the type looks like &ActorRef<Self> or &rsactor::ActorRef<Self>
418 matches!(ty.as_ref(), Type::Reference(_))
419 }
420 _ => false,
421 };
422
423 if !third_param_valid {
424 return Err(syn::Error::new_spanned(
425 &inputs[2],
426 "Third parameter must be '&ActorRef<Self>' or '&rsactor::ActorRef<Self>'",
427 ));
428 }
429
430 // Extract return type - handle both explicit return types and unit return (no return type)
431 let return_type = match &method.sig.output {
432 ReturnType::Type(_, ty) => quote! { #ty },
433 ReturnType::Default => quote! { () }, // Default to unit type for functions without explicit return type
434 };
435
436 // Get method name
437 let method_name = &method.sig.ident;
438
439 let (impl_generics, _ty_generics, where_clause) = generics.split_for_impl();
440
441 // Generate the Message trait implementation
442 let impl_tokens = quote! {
443 impl #impl_generics rsactor::Message<#message_type> for #actor_type #where_clause {
444 type Reply = #return_type;
445
446 async fn handle(
447 &mut self,
448 msg: #message_type,
449 actor_ref: &rsactor::ActorRef<Self>,
450 ) -> Self::Reply {
451 self.#method_name(msg, actor_ref).await
452 }
453 }
454 };
455
456 Ok(impl_tokens)
457}
458
459// TODO: Future enhancements that could be added:
460//
461// 1. Support for custom error types in derive macro:
462// #[derive(Actor)]
463// #[actor(error = "MyCustomError")]
464// struct MyActor { ... }
465//
466// 2. Support for custom Args types:
467// #[derive(Actor)]
468// #[actor(args = "MyArgsType")]
469// struct MyActor { ... }
470//
471// 3. Handler attribute with options:
472// #[handler(timeout = "5s")]
473// #[handler(priority = "high")]
474// async fn handle_message(&mut self, msg: Msg, _: &ActorRef<Self>) -> Reply
475//
476// 4. Automatic message struct generation:
477// #[message_handlers]
478// impl MyActor {
479// #[handler]
480// #[message(name = "Increment")] // Generates struct Increment;
481// async fn handle_increment(&mut self, _: (), _: &ActorRef<Self>) -> u32
482// }
483//
484// 5. Validation attributes:
485// #[handler]
486// #[validate(non_empty, range(1..100))]
487// async fn handle_set_value(&mut self, msg: SetValue, _: &ActorRef<Self>) -> Result<(), Error>