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