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>