Skip to main content

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
321/// Options parsed from the `#[handler]` attribute.
322#[derive(Default)]
323struct HandlerOptions {
324    /// `#[handler(result)]` — treat return type as Result and generate on_tell_result override.
325    force_result: bool,
326    /// `#[handler(no_log)]` — suppress automatic on_tell_result generation.
327    no_log: bool,
328}
329
330fn parse_handler_options(attr: &syn::Attribute) -> syn::Result<HandlerOptions> {
331    let mut options = HandlerOptions::default();
332
333    match &attr.meta {
334        // #[handler] — no arguments
335        syn::Meta::Path(_) => {}
336
337        // #[handler(...)] — parse arguments
338        syn::Meta::List(_) => {
339            attr.parse_nested_meta(|meta| {
340                if meta.path.is_ident("result") {
341                    options.force_result = true;
342                    Ok(())
343                } else if meta.path.is_ident("no_log") {
344                    options.no_log = true;
345                    Ok(())
346                } else {
347                    Err(meta.error("unknown handler option; expected `result` or `no_log`"))
348                }
349            })?;
350        }
351
352        _ => {
353            return Err(syn::Error::new_spanned(
354                attr,
355                "expected `#[handler]`, `#[handler(result)]`, or `#[handler(no_log)]`",
356            ));
357        }
358    }
359
360    if options.force_result && options.no_log {
361        return Err(syn::Error::new_spanned(
362            attr,
363            "`result` and `no_log` are mutually exclusive",
364        ));
365    }
366
367    Ok(options)
368}
369
370/// Returns true if the type is syntactically `Result<...>`.
371///
372/// Detectable: `Result<T, E>`, `std::result::Result<T, E>`, `anyhow::Result<T>`
373/// Not detectable: type aliases (e.g., `MyResult`) — use `#[handler(result)]` instead.
374///
375/// Note: only compares the last path segment's ident, so a user-defined type named
376/// `Result` will produce a false positive. Use `#[handler(no_log)]` to suppress.
377fn is_result_type(ty: &syn::Type) -> bool {
378    match ty {
379        syn::Type::Path(type_path) => type_path
380            .path
381            .segments
382            .last()
383            .map(|seg| seg.ident == "Result")
384            .unwrap_or(false),
385        _ => false,
386    }
387}
388
389fn process_handler_methods(
390    items: &[ImplItem],
391    actor_type: &Type,
392    generics: &syn::Generics,
393) -> syn::Result<Vec<TokenStream2>> {
394    let mut message_impls = Vec::new();
395
396    for item in items {
397        if let ImplItem::Fn(method) = item {
398            let handler_attr = method
399                .attrs
400                .iter()
401                .find(|attr| attr.path().is_ident("handler"));
402
403            if let Some(attr) = handler_attr {
404                let options = parse_handler_options(attr)?;
405                let impl_tokens = generate_message_impl(method, actor_type, generics, &options)?;
406                message_impls.push(impl_tokens);
407            }
408        }
409    }
410
411    Ok(message_impls)
412}
413
414fn clean_handler_attributes(items: &mut [ImplItem]) {
415    for item in items {
416        if let ImplItem::Fn(method) = item {
417            method.attrs.retain(|attr| !attr.path().is_ident("handler"));
418        }
419    }
420}
421
422fn generate_message_impl(
423    method: &ImplItemFn,
424    actor_type: &Type,
425    generics: &syn::Generics,
426    options: &HandlerOptions,
427) -> syn::Result<TokenStream2> {
428    // Parse method signature
429    let inputs = &method.sig.inputs;
430
431    // Validate that the method is async
432    if method.sig.asyncness.is_none() {
433        return Err(syn::Error::new_spanned(
434            &method.sig,
435            format!("Handler method '{}' must be async", method.sig.ident),
436        ));
437    }
438
439    if inputs.len() != 3 {
440        return Err(syn::Error::new_spanned(
441            &method.sig,
442            format!(
443                "Message handler method '{}' must have exactly 3 parameters: &mut self, message, &ActorRef<Self>. Found {} parameters.",
444                method.sig.ident,
445                inputs.len()
446            )
447        ));
448    }
449
450    // Validate first parameter (&mut self)
451    if !matches!(&inputs[0], FnArg::Receiver(receiver) if receiver.mutability.is_some()) {
452        return Err(syn::Error::new_spanned(
453            &inputs[0],
454            "First parameter must be '&mut self'",
455        ));
456    }
457
458    // Extract message type from second parameter
459    let message_type = match &inputs[1] {
460        FnArg::Typed(PatType { ty, .. }) => ty,
461        _ => {
462            return Err(syn::Error::new_spanned(
463                &inputs[1],
464                "Second parameter must be a typed message parameter (e.g., 'msg: MessageType')",
465            ))
466        }
467    };
468
469    // Validate third parameter (&ActorRef<Self>)
470    let third_param_valid = match &inputs[2] {
471        FnArg::Typed(PatType { ty, .. }) => {
472            // Check if the type looks like &ActorRef<Self> or &rsactor::ActorRef<Self>
473            matches!(ty.as_ref(), Type::Reference(_))
474        }
475        _ => false,
476    };
477
478    if !third_param_valid {
479        return Err(syn::Error::new_spanned(
480            &inputs[2],
481            "Third parameter must be '&ActorRef<Self>' or '&rsactor::ActorRef<Self>'",
482        ));
483    }
484
485    // Extract return type
486    let return_type_ty = match &method.sig.output {
487        ReturnType::Type(_, ty) => Some(ty.as_ref()),
488        ReturnType::Default => None,
489    };
490
491    let return_type = match return_type_ty {
492        Some(ty) => quote! { #ty },
493        None => quote! { () },
494    };
495
496    // Determine whether to generate on_tell_result override
497    let is_result = return_type_ty.map(is_result_type).unwrap_or(false);
498
499    let should_generate_on_tell_result = if options.no_log {
500        false
501    } else if options.force_result {
502        if return_type_ty.is_none() {
503            return Err(syn::Error::new_spanned(
504                &method.sig,
505                "`#[handler(result)]` requires a return type, but this method returns `()`",
506            ));
507        }
508        true
509    } else {
510        is_result
511    };
512
513    // Get method name
514    let method_name = &method.sig.ident;
515
516    let (impl_generics, _ty_generics, where_clause) = generics.split_for_impl();
517
518    let on_tell_result_impl = if should_generate_on_tell_result {
519        quote! {
520            fn on_tell_result(result: &Self::Reply, actor_ref: &rsactor::ActorRef<Self>) {
521                if let Err(ref e) = result {
522                    tracing::error!(
523                        actor = %actor_ref.identity(),
524                        message_type = %std::any::type_name::<#message_type>(),
525                        "tell handler returned error: {}", e
526                    );
527                }
528            }
529        }
530    } else {
531        quote! {}
532    };
533
534    // Generate the Message trait implementation
535    let impl_tokens = quote! {
536        impl #impl_generics rsactor::Message<#message_type> for #actor_type #where_clause {
537            type Reply = #return_type;
538
539            async fn handle(
540                &mut self,
541                msg: #message_type,
542                actor_ref: &rsactor::ActorRef<Self>,
543            ) -> Self::Reply {
544                self.#method_name(msg, actor_ref).await
545            }
546
547            #on_tell_result_impl
548        }
549    };
550
551    Ok(impl_tokens)
552}
553
554// TODO: Future enhancements that could be added:
555//
556// 1. Support for custom error types in derive macro:
557//    #[derive(Actor)]
558//    #[actor(error = "MyCustomError")]
559//    struct MyActor { ... }
560//
561// 2. Support for custom Args types:
562//    #[derive(Actor)]
563//    #[actor(args = "MyArgsType")]
564//    struct MyActor { ... }
565//
566// 3. Handler attribute with options:
567//    #[handler(timeout = "5s")]
568//    #[handler(priority = "high")]
569//    async fn handle_message(&mut self, msg: Msg, _: &ActorRef<Self>) -> Reply
570//
571// 4. Automatic message struct generation:
572//    #[message_handlers]
573//    impl MyActor {
574//        #[handler]
575//        #[message(name = "Increment")]  // Generates struct Increment;
576//        async fn handle_increment(&mut self, _: (), _: &ActorRef<Self>) -> u32
577//    }
578//
579// 5. Validation attributes:
580//    #[handler]
581//    #[validate(non_empty, range(1..100))]
582//    async fn handle_set_value(&mut self, msg: SetValue, _: &ActorRef<Self>) -> Result<(), Error>