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//!     type IdleEvent = ();
57//!
58//!     async fn on_start(
59//!         args: Self::Args,
60//!         _actor_ref: &ActorRef<Self>,
61//!     ) -> std::result::Result<Self, Self::Error> {
62//!         Ok(args)
63//!     }
64//! }
65//! ```
66//!
67//! ### When to Use
68//!
69//! Use the derive macro when:
70//! - Your actor doesn't need complex initialization
71//! - You want to pass a fully constructed instance to `spawn()`
72//! - You don't need custom error handling during initialization
73//!
74//! For complex initialization (async resource setup, validation, etc.),
75//! implement the Actor trait manually.
76//!
77//! ## Message Handlers Attribute Macro
78//!
79//! The `#[message_handlers]` attribute macro combined with `#[handler]` method attributes
80//! provides an automated way to generate Message trait implementations and register message handlers.
81//!
82//! ### Usage
83//!
84//! ```rust
85//! use rsactor::{Actor, ActorRef, message_handlers};
86//!
87//! #[derive(Actor)]
88//! struct MyActor {
89//!     count: u32,
90//! }
91//!
92//! struct Increment;
93//! struct GetCount;
94//!
95//! #[message_handlers]
96//! impl MyActor {
97//!     #[handler]
98//!     async fn handle_increment(&mut self, _msg: Increment, _: &ActorRef<Self>) -> u32 {
99//!         self.count += 1;
100//!         self.count
101//!     }
102//!
103//!     #[handler]
104//!     async fn handle_get_count(&mut self, _msg: GetCount, _: &ActorRef<Self>) -> u32 {
105//!         self.count
106//!     }
107//!
108//!     // Regular methods without #[handler] are left unchanged
109//!     fn internal_method(&self) -> u32 {
110//!         self.count * 2
111//!     }
112//! }
113//! ```
114//!
115//! ### Benefits
116//!
117//! - Automatic generation of `Message<T>` trait implementations
118//! - Selective processing: only methods with `#[handler]` attribute are processed
119//! - Reduced boilerplate and potential for errors
120//! - Type-safe message handling with compile-time checks
121
122use proc_macro::TokenStream;
123use proc_macro2::TokenStream as TokenStream2;
124use quote::quote;
125use syn::{
126    parse_macro_input, Data, DeriveInput, FnArg, ImplItem, ImplItemFn, ItemImpl, PatType,
127    ReturnType, Type,
128};
129
130/// Derive macro for automatically implementing the Actor trait.
131///
132/// This macro generates a default implementation of the Actor trait where:
133/// - `Args` type is set to `Self`
134/// - `Error` type is set to `std::convert::Infallible`
135/// - `on_start` method returns the args as the actor instance
136///
137/// # Examples
138///
139/// ## Struct Actor
140/// ```rust
141/// use rsactor::Actor;
142///
143/// #[derive(Actor)]
144/// struct MyActor {
145///     name: String,
146/// }
147/// ```
148///
149/// ## Enum Actor
150/// ```rust
151/// use rsactor::Actor;
152///
153/// #[derive(Actor)]
154/// enum StateActor {
155///     Idle,
156///     Processing(String),
157///     Completed(i32),
158/// }
159/// ```
160///
161/// This generates an implementation equivalent to:
162///
163/// ```rust
164/// # struct MyActor { name: String }
165/// impl rsactor::Actor for MyActor {
166///     type Args = Self;
167///     type Error = std::convert::Infallible;
168///     type IdleEvent = ();
169///
170///     async fn on_start(
171///         args: Self::Args,
172///         _actor_ref: &rsactor::ActorRef<Self>,
173///     ) -> std::result::Result<Self, Self::Error> {
174///         Ok(args)
175///     }
176/// }
177/// ```
178///
179/// # Limitations
180///
181/// - Only works on structs and enums (not unions)
182/// - Generates a very basic implementation - for complex initialization logic,
183///   implement the Actor trait manually
184#[proc_macro_derive(Actor)]
185pub fn derive_actor(input: TokenStream) -> TokenStream {
186    let input = parse_macro_input!(input as DeriveInput);
187
188    let expanded = match derive_actor_impl(input) {
189        Ok(tokens) => tokens,
190        Err(err) => err.to_compile_error(),
191    };
192
193    TokenStream::from(expanded)
194}
195
196fn derive_actor_impl(input: DeriveInput) -> syn::Result<TokenStream2> {
197    let name = &input.ident;
198    let generics = &input.generics;
199    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
200
201    // Check if it's a struct or enum
202    match &input.data {
203        Data::Struct(_) | Data::Enum(_) => {
204            // Generate the Actor implementation with proper generic support
205            let expanded = quote! {
206                impl #impl_generics rsactor::Actor for #name #ty_generics #where_clause {
207                    type Args = Self;
208                    type Error = std::convert::Infallible;
209                    type IdleEvent = ();
210
211                    async fn on_start(
212                        args: Self::Args,
213                        _actor_ref: &rsactor::ActorRef<Self>,
214                    ) -> std::result::Result<Self, Self::Error> {
215                        Ok(args)
216                    }
217                }
218            };
219
220            Ok(expanded)
221        }
222        _ => Err(syn::Error::new_spanned(
223            name,
224            "Actor derive macro can only be used on structs and enums",
225        )),
226    }
227}
228
229/// Attribute macro for automatically generating Message trait implementations
230/// from method definitions.
231///
232/// This macro analyzes method signatures in an impl block and generates the corresponding
233/// Message trait implementations for methods marked with `#[handler]` attribute, reducing
234/// boilerplate code.
235///
236/// # Usage
237///
238/// ```rust
239/// use rsactor::{Actor, ActorRef, message_handlers};
240///
241/// #[derive(Actor)]
242/// struct MyActor {
243///     count: u32,
244/// }
245///
246/// struct Increment;
247/// struct Decrement;
248///
249/// #[message_handlers]
250/// impl MyActor {
251///     #[handler]
252///     async fn handle_increment(&mut self, _msg: Increment, _: &ActorRef<Self>) -> u32 {
253///         self.count += 1;
254///         self.count
255///     }
256///
257///     #[handler]
258///     async fn handle_decrement(&mut self, _msg: Decrement, _: &ActorRef<Self>) -> u32 {
259///         self.count -= 1;
260///         self.count
261///     }
262///
263///     // Regular methods without #[handler] are left unchanged
264///     fn get_internal_state(&self) -> u32 {
265///         self.count
266///     }
267/// }
268/// ```
269///
270/// This will automatically generate:
271/// - The `Message<MessageType>` trait implementations for each handler method
272///
273/// # Requirements
274///
275/// Each method marked with `#[handler]` must follow this signature pattern:
276/// - Must be an `async fn`
277/// - First parameter: `&mut self`
278/// - Second parameter: `msg: MessageType` (where MessageType is the message struct)
279/// - Third parameter: `&ActorRef<Self>` or `&rsactor::ActorRef<Self>`
280/// - Return type: the reply type for the message
281///
282/// # Error Messages
283///
284/// The macro provides detailed error messages for common mistakes:
285/// - Wrong parameter count
286/// - Missing `async` keyword
287/// - Incorrect parameter types
288/// - Invalid #[handler] attribute usage
289///
290/// # Benefits
291///
292/// - No need to manually implement `Message<T>` trait for each message type
293/// - Reduces boilerplate code and potential for errors
294/// - Only processes methods marked with `#[handler]`, leaving other methods unchanged
295#[proc_macro_attribute]
296pub fn message_handlers(_attr: TokenStream, item: TokenStream) -> TokenStream {
297    let input = parse_macro_input!(item as ItemImpl);
298
299    let expanded = match message_impl(input) {
300        Ok(tokens) => tokens,
301        Err(err) => err.to_compile_error(),
302    };
303
304    TokenStream::from(expanded)
305}
306
307fn message_impl(mut input: ItemImpl) -> syn::Result<TokenStream2> {
308    let actor_type = &input.self_ty;
309    let generics = &input.generics;
310
311    let message_impls = process_handler_methods(&input.items, actor_type, generics)?;
312
313    // Remove `#[handler]` attributes from the impl block for clean output
314    clean_handler_attributes(&mut input.items);
315
316    let result = quote! {
317        #input
318        #(#message_impls)*
319    };
320
321    Ok(result)
322}
323
324/// Options parsed from the `#[handler]` attribute.
325#[derive(Default)]
326struct HandlerOptions {
327    /// `#[handler(result)]` — treat return type as Result and generate on_tell_result override.
328    force_result: bool,
329    /// `#[handler(no_log)]` — suppress automatic on_tell_result generation.
330    no_log: bool,
331}
332
333fn parse_handler_options(attr: &syn::Attribute) -> syn::Result<HandlerOptions> {
334    let mut options = HandlerOptions::default();
335
336    match &attr.meta {
337        // #[handler] — no arguments
338        syn::Meta::Path(_) => {}
339
340        // #[handler(...)] — parse arguments
341        syn::Meta::List(_) => {
342            attr.parse_nested_meta(|meta| {
343                if meta.path.is_ident("result") {
344                    options.force_result = true;
345                    Ok(())
346                } else if meta.path.is_ident("no_log") {
347                    options.no_log = true;
348                    Ok(())
349                } else {
350                    Err(meta.error("unknown handler option; expected `result` or `no_log`"))
351                }
352            })?;
353        }
354
355        _ => {
356            return Err(syn::Error::new_spanned(
357                attr,
358                "expected `#[handler]`, `#[handler(result)]`, or `#[handler(no_log)]`",
359            ));
360        }
361    }
362
363    if options.force_result && options.no_log {
364        return Err(syn::Error::new_spanned(
365            attr,
366            "`result` and `no_log` are mutually exclusive",
367        ));
368    }
369
370    Ok(options)
371}
372
373/// Returns true if the type is syntactically `Result<...>`.
374///
375/// Detectable: `Result<T, E>`, `std::result::Result<T, E>`, `anyhow::Result<T>`
376/// Not detectable: type aliases (e.g., `MyResult`) — use `#[handler(result)]` instead.
377///
378/// Note: only compares the last path segment's ident, so a user-defined type named
379/// `Result` will produce a false positive. Use `#[handler(no_log)]` to suppress.
380fn is_result_type(ty: &syn::Type) -> bool {
381    match ty {
382        syn::Type::Path(type_path) => type_path
383            .path
384            .segments
385            .last()
386            .map(|seg| seg.ident == "Result")
387            .unwrap_or(false),
388        _ => false,
389    }
390}
391
392fn process_handler_methods(
393    items: &[ImplItem],
394    actor_type: &Type,
395    generics: &syn::Generics,
396) -> syn::Result<Vec<TokenStream2>> {
397    let mut message_impls = Vec::new();
398
399    for item in items {
400        if let ImplItem::Fn(method) = item {
401            let handler_attr = method
402                .attrs
403                .iter()
404                .find(|attr| attr.path().is_ident("handler"));
405
406            if let Some(attr) = handler_attr {
407                let options = parse_handler_options(attr)?;
408                let impl_tokens = generate_message_impl(method, actor_type, generics, &options)?;
409                message_impls.push(impl_tokens);
410            }
411        }
412    }
413
414    Ok(message_impls)
415}
416
417fn clean_handler_attributes(items: &mut [ImplItem]) {
418    for item in items {
419        if let ImplItem::Fn(method) = item {
420            method.attrs.retain(|attr| !attr.path().is_ident("handler"));
421        }
422    }
423}
424
425fn generate_message_impl(
426    method: &ImplItemFn,
427    actor_type: &Type,
428    generics: &syn::Generics,
429    options: &HandlerOptions,
430) -> syn::Result<TokenStream2> {
431    // Parse method signature
432    let inputs = &method.sig.inputs;
433
434    // Validate that the method is async
435    if method.sig.asyncness.is_none() {
436        return Err(syn::Error::new_spanned(
437            &method.sig,
438            format!("Handler method '{}' must be async", method.sig.ident),
439        ));
440    }
441
442    if inputs.len() != 3 {
443        return Err(syn::Error::new_spanned(
444            &method.sig,
445            format!(
446                "Message handler method '{}' must have exactly 3 parameters: &mut self, message, &ActorRef<Self>. Found {} parameters.",
447                method.sig.ident,
448                inputs.len()
449            )
450        ));
451    }
452
453    // Validate first parameter (&mut self)
454    if !matches!(&inputs[0], FnArg::Receiver(receiver) if receiver.mutability.is_some()) {
455        return Err(syn::Error::new_spanned(
456            &inputs[0],
457            "First parameter must be '&mut self'",
458        ));
459    }
460
461    // Extract message type from second parameter
462    let message_type = match &inputs[1] {
463        FnArg::Typed(PatType { ty, .. }) => ty,
464        _ => {
465            return Err(syn::Error::new_spanned(
466                &inputs[1],
467                "Second parameter must be a typed message parameter (e.g., 'msg: MessageType')",
468            ))
469        }
470    };
471
472    // Validate third parameter (&ActorRef<Self>)
473    let third_param_valid = match &inputs[2] {
474        FnArg::Typed(PatType { ty, .. }) => {
475            match ty.as_ref() {
476                Type::Reference(type_ref) => {
477                    // Check if the referenced type contains "ActorRef" in its path
478                    if let Type::Path(type_path) = type_ref.elem.as_ref() {
479                        type_path
480                            .path
481                            .segments
482                            .iter()
483                            .any(|seg| seg.ident == "ActorRef")
484                    } else {
485                        false
486                    }
487                }
488                _ => false,
489            }
490        }
491        _ => false,
492    };
493
494    if !third_param_valid {
495        return Err(syn::Error::new_spanned(
496            &inputs[2],
497            "Third parameter must be '&ActorRef<Self>' or '&rsactor::ActorRef<Self>'",
498        ));
499    }
500
501    // Extract return type
502    let return_type_ty = match &method.sig.output {
503        ReturnType::Type(_, ty) => Some(ty.as_ref()),
504        ReturnType::Default => None,
505    };
506
507    let return_type = match return_type_ty {
508        Some(ty) => quote! { #ty },
509        None => quote! { () },
510    };
511
512    // Determine whether to generate on_tell_result override
513    let is_result = return_type_ty.map(is_result_type).unwrap_or(false);
514
515    let should_generate_on_tell_result = if options.no_log {
516        false
517    } else if options.force_result {
518        if return_type_ty.is_none() {
519            return Err(syn::Error::new_spanned(
520                &method.sig,
521                "`#[handler(result)]` requires a return type, but this method returns `()`",
522            ));
523        }
524        true
525    } else {
526        is_result
527    };
528
529    // Get method name
530    let method_name = &method.sig.ident;
531
532    let (impl_generics, _ty_generics, where_clause) = generics.split_for_impl();
533
534    let on_tell_result_impl = if should_generate_on_tell_result {
535        quote! {
536            fn on_tell_result(result: &Self::Reply, actor_ref: &rsactor::ActorRef<Self>) {
537                if let Err(ref e) = result {
538                    rsactor::__log_handler_error(
539                        &actor_ref.identity(),
540                        std::any::type_name::<#message_type>(),
541                        e
542                    );
543                }
544            }
545        }
546    } else {
547        quote! {}
548    };
549
550    // Generate the Message trait implementation
551    let impl_tokens = quote! {
552        impl #impl_generics rsactor::Message<#message_type> for #actor_type #where_clause {
553            type Reply = #return_type;
554
555            async fn handle(
556                &mut self,
557                msg: #message_type,
558                actor_ref: &rsactor::ActorRef<Self>,
559            ) -> Self::Reply {
560                self.#method_name(msg, actor_ref).await
561            }
562
563            #on_tell_result_impl
564        }
565    };
566
567    Ok(impl_tokens)
568}
569
570// TODO: Future enhancements that could be added:
571//
572// 1. Support for custom error types in derive macro:
573//    #[derive(Actor)]
574//    #[actor(error = "MyCustomError")]
575//    struct MyActor { ... }
576//
577// 2. Support for custom Args types:
578//    #[derive(Actor)]
579//    #[actor(args = "MyArgsType")]
580//    struct MyActor { ... }
581//
582// 3. Handler attribute with options:
583//    #[handler(timeout = "5s")]
584//    #[handler(priority = "high")]
585//    async fn handle_message(&mut self, msg: Msg, _: &ActorRef<Self>) -> Reply
586//
587// 4. Automatic message struct generation:
588//    #[message_handlers]
589//    impl MyActor {
590//        #[handler]
591//        #[message(name = "Increment")]  // Generates struct Increment;
592//        async fn handle_increment(&mut self, _: (), _: &ActorRef<Self>) -> u32
593//    }
594//
595// 5. Validation attributes:
596//    #[handler]
597//    #[validate(non_empty, range(1..100))]
598//    async fn handle_set_value(&mut self, msg: SetValue, _: &ActorRef<Self>) -> Result<(), Error>