rsactor-derive 0.14.1

Derive macros for rsActor framework
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
// Copyright 2022 Jeff Kim <hiking90@gmail.com>
// SPDX-License-Identifier: Apache-2.0

//! Derive macros for rsActor framework
//!
//! This crate provides derive macros for the rsActor framework, allowing users
//! to automatically implement common traits with sensible defaults.
//!
//! ## Actor Derive Macro
//!
//! The `#[derive(Actor)]` macro provides a convenient way to implement the Actor trait
//! for simple structs and enums that don't require complex initialization logic.
//!
//! ### Generated Implementation
//!
//! When you use `#[derive(Actor)]`, it generates:
//! - `Args` type set to `Self` (the struct or enum itself)
//! - `Error` type set to `std::convert::Infallible` (never fails)
//! - `on_start` method that simply returns the provided args
//!
//! ### Usage
//!
//! #### With Structs
//! ```rust
//! use rsactor::Actor;
//!
//! #[derive(Actor)]
//! struct MyActor {
//!     name: String,
//!     count: u32,
//! }
//! ```
//!
//! #### With Enums
//! ```rust
//! use rsactor::Actor;
//!
//! #[derive(Actor)]
//! enum StateActor {
//!     Idle,
//!     Processing(String),
//!     Completed(i32),
//! }
//! ```
//!
//! This is equivalent to manually writing:
//!
//! ```rust
//! # struct MyActor { name: String, count: u32 }
//! use rsactor::{Actor, ActorRef};
//! use std::convert::Infallible;
//!
//! impl Actor for MyActor {
//!     type Args = Self;
//!     type Error = Infallible;
//!
//!     async fn on_start(
//!         args: Self::Args,
//!         _actor_ref: &ActorRef<Self>,
//!     ) -> std::result::Result<Self, Self::Error> {
//!         Ok(args)
//!     }
//! }
//! ```
//!
//! ### When to Use
//!
//! Use the derive macro when:
//! - Your actor doesn't need complex initialization
//! - You want to pass a fully constructed instance to `spawn()`
//! - You don't need custom error handling during initialization
//!
//! For complex initialization (async resource setup, validation, etc.),
//! implement the Actor trait manually.
//!
//! ## Message Handlers Attribute Macro
//!
//! The `#[message_handlers]` attribute macro combined with `#[handler]` method attributes
//! provides an automated way to generate Message trait implementations and register message handlers.
//!
//! ### Usage
//!
//! ```rust
//! use rsactor::{Actor, ActorRef, message_handlers};
//!
//! #[derive(Actor)]
//! struct MyActor {
//!     count: u32,
//! }
//!
//! struct Increment;
//! struct GetCount;
//!
//! #[message_handlers]
//! impl MyActor {
//!     #[handler]
//!     async fn handle_increment(&mut self, _msg: Increment, _: &ActorRef<Self>) -> u32 {
//!         self.count += 1;
//!         self.count
//!     }
//!
//!     #[handler]
//!     async fn handle_get_count(&mut self, _msg: GetCount, _: &ActorRef<Self>) -> u32 {
//!         self.count
//!     }
//!
//!     // Regular methods without #[handler] are left unchanged
//!     fn internal_method(&self) -> u32 {
//!         self.count * 2
//!     }
//! }
//! ```
//!
//! ### Benefits
//!
//! - Automatic generation of `Message<T>` trait implementations
//! - Selective processing: only methods with `#[handler]` attribute are processed
//! - Reduced boilerplate and potential for errors
//! - Type-safe message handling with compile-time checks

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{
    parse_macro_input, Data, DeriveInput, FnArg, ImplItem, ImplItemFn, ItemImpl, PatType,
    ReturnType, Type,
};

/// Derive macro for automatically implementing the Actor trait.
///
/// This macro generates a default implementation of the Actor trait where:
/// - `Args` type is set to `Self`
/// - `Error` type is set to `std::convert::Infallible`
/// - `on_start` method returns the args as the actor instance
///
/// # Examples
///
/// ## Struct Actor
/// ```rust
/// use rsactor::Actor;
///
/// #[derive(Actor)]
/// struct MyActor {
///     name: String,
/// }
/// ```
///
/// ## Enum Actor
/// ```rust
/// use rsactor::Actor;
///
/// #[derive(Actor)]
/// enum StateActor {
///     Idle,
///     Processing(String),
///     Completed(i32),
/// }
/// ```
///
/// This generates an implementation equivalent to:
///
/// ```rust
/// # struct MyActor { name: String }
/// impl rsactor::Actor for MyActor {
///     type Args = Self;
///     type Error = std::convert::Infallible;
///
///     async fn on_start(
///         args: Self::Args,
///         _actor_ref: &rsactor::ActorRef<Self>,
///     ) -> std::result::Result<Self, Self::Error> {
///         Ok(args)
///     }
/// }
/// ```
///
/// # Limitations
///
/// - Only works on structs and enums (not unions)
/// - Generates a very basic implementation - for complex initialization logic,
///   implement the Actor trait manually
#[proc_macro_derive(Actor)]
pub fn derive_actor(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    let expanded = match derive_actor_impl(input) {
        Ok(tokens) => tokens,
        Err(err) => err.to_compile_error(),
    };

    TokenStream::from(expanded)
}

fn derive_actor_impl(input: DeriveInput) -> syn::Result<TokenStream2> {
    let name = &input.ident;
    let generics = &input.generics;
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

    // Check if it's a struct or enum
    match &input.data {
        Data::Struct(_) | Data::Enum(_) => {
            // Generate the Actor implementation with proper generic support
            let expanded = quote! {
                impl #impl_generics rsactor::Actor for #name #ty_generics #where_clause {
                    type Args = Self;
                    type Error = std::convert::Infallible;

                    async fn on_start(
                        args: Self::Args,
                        _actor_ref: &rsactor::ActorRef<Self>,
                    ) -> std::result::Result<Self, Self::Error> {
                        Ok(args)
                    }
                }
            };

            Ok(expanded)
        }
        _ => Err(syn::Error::new_spanned(
            name,
            "Actor derive macro can only be used on structs and enums",
        )),
    }
}

/// Attribute macro for automatically generating Message trait implementations
/// from method definitions.
///
/// This macro analyzes method signatures in an impl block and generates the corresponding
/// Message trait implementations for methods marked with `#[handler]` attribute, reducing
/// boilerplate code.
///
/// # Usage
///
/// ```rust
/// use rsactor::{Actor, ActorRef, message_handlers};
///
/// #[derive(Actor)]
/// struct MyActor {
///     count: u32,
/// }
///
/// struct Increment;
/// struct Decrement;
///
/// #[message_handlers]
/// impl MyActor {
///     #[handler]
///     async fn handle_increment(&mut self, _msg: Increment, _: &ActorRef<Self>) -> u32 {
///         self.count += 1;
///         self.count
///     }
///
///     #[handler]
///     async fn handle_decrement(&mut self, _msg: Decrement, _: &ActorRef<Self>) -> u32 {
///         self.count -= 1;
///         self.count
///     }
///
///     // Regular methods without #[handler] are left unchanged
///     fn get_internal_state(&self) -> u32 {
///         self.count
///     }
/// }
/// ```
///
/// This will automatically generate:
/// - The `Message<MessageType>` trait implementations for each handler method
///
/// # Requirements
///
/// Each method marked with `#[handler]` must follow this signature pattern:
/// - Must be an `async fn`
/// - First parameter: `&mut self`
/// - Second parameter: `msg: MessageType` (where MessageType is the message struct)
/// - Third parameter: `&ActorRef<Self>` or `&rsactor::ActorRef<Self>`
/// - Return type: the reply type for the message
///
/// # Error Messages
///
/// The macro provides detailed error messages for common mistakes:
/// - Wrong parameter count
/// - Missing `async` keyword
/// - Incorrect parameter types
/// - Invalid #[handler] attribute usage
///
/// # Benefits
///
/// - No need to manually implement `Message<T>` trait for each message type
/// - Reduces boilerplate code and potential for errors
/// - Only processes methods marked with `#[handler]`, leaving other methods unchanged
#[proc_macro_attribute]
pub fn message_handlers(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let input = parse_macro_input!(item as ItemImpl);

    let expanded = match message_impl(input) {
        Ok(tokens) => tokens,
        Err(err) => err.to_compile_error(),
    };

    TokenStream::from(expanded)
}

fn message_impl(mut input: ItemImpl) -> syn::Result<TokenStream2> {
    let actor_type = &input.self_ty;
    let generics = &input.generics;

    let message_impls = process_handler_methods(&input.items, actor_type, generics)?;

    // Remove `#[handler]` attributes from the impl block for clean output
    clean_handler_attributes(&mut input.items);

    let result = quote! {
        #input
        #(#message_impls)*
    };

    Ok(result)
}

/// Options parsed from the `#[handler]` attribute.
#[derive(Default)]
struct HandlerOptions {
    /// `#[handler(result)]` — treat return type as Result and generate on_tell_result override.
    force_result: bool,
    /// `#[handler(no_log)]` — suppress automatic on_tell_result generation.
    no_log: bool,
}

fn parse_handler_options(attr: &syn::Attribute) -> syn::Result<HandlerOptions> {
    let mut options = HandlerOptions::default();

    match &attr.meta {
        // #[handler] — no arguments
        syn::Meta::Path(_) => {}

        // #[handler(...)] — parse arguments
        syn::Meta::List(_) => {
            attr.parse_nested_meta(|meta| {
                if meta.path.is_ident("result") {
                    options.force_result = true;
                    Ok(())
                } else if meta.path.is_ident("no_log") {
                    options.no_log = true;
                    Ok(())
                } else {
                    Err(meta.error("unknown handler option; expected `result` or `no_log`"))
                }
            })?;
        }

        _ => {
            return Err(syn::Error::new_spanned(
                attr,
                "expected `#[handler]`, `#[handler(result)]`, or `#[handler(no_log)]`",
            ));
        }
    }

    if options.force_result && options.no_log {
        return Err(syn::Error::new_spanned(
            attr,
            "`result` and `no_log` are mutually exclusive",
        ));
    }

    Ok(options)
}

/// Returns true if the type is syntactically `Result<...>`.
///
/// Detectable: `Result<T, E>`, `std::result::Result<T, E>`, `anyhow::Result<T>`
/// Not detectable: type aliases (e.g., `MyResult`) — use `#[handler(result)]` instead.
///
/// Note: only compares the last path segment's ident, so a user-defined type named
/// `Result` will produce a false positive. Use `#[handler(no_log)]` to suppress.
fn is_result_type(ty: &syn::Type) -> bool {
    match ty {
        syn::Type::Path(type_path) => type_path
            .path
            .segments
            .last()
            .map(|seg| seg.ident == "Result")
            .unwrap_or(false),
        _ => false,
    }
}

fn process_handler_methods(
    items: &[ImplItem],
    actor_type: &Type,
    generics: &syn::Generics,
) -> syn::Result<Vec<TokenStream2>> {
    let mut message_impls = Vec::new();

    for item in items {
        if let ImplItem::Fn(method) = item {
            let handler_attr = method
                .attrs
                .iter()
                .find(|attr| attr.path().is_ident("handler"));

            if let Some(attr) = handler_attr {
                let options = parse_handler_options(attr)?;
                let impl_tokens = generate_message_impl(method, actor_type, generics, &options)?;
                message_impls.push(impl_tokens);
            }
        }
    }

    Ok(message_impls)
}

fn clean_handler_attributes(items: &mut [ImplItem]) {
    for item in items {
        if let ImplItem::Fn(method) = item {
            method.attrs.retain(|attr| !attr.path().is_ident("handler"));
        }
    }
}

fn generate_message_impl(
    method: &ImplItemFn,
    actor_type: &Type,
    generics: &syn::Generics,
    options: &HandlerOptions,
) -> syn::Result<TokenStream2> {
    // Parse method signature
    let inputs = &method.sig.inputs;

    // Validate that the method is async
    if method.sig.asyncness.is_none() {
        return Err(syn::Error::new_spanned(
            &method.sig,
            format!("Handler method '{}' must be async", method.sig.ident),
        ));
    }

    if inputs.len() != 3 {
        return Err(syn::Error::new_spanned(
            &method.sig,
            format!(
                "Message handler method '{}' must have exactly 3 parameters: &mut self, message, &ActorRef<Self>. Found {} parameters.",
                method.sig.ident,
                inputs.len()
            )
        ));
    }

    // Validate first parameter (&mut self)
    if !matches!(&inputs[0], FnArg::Receiver(receiver) if receiver.mutability.is_some()) {
        return Err(syn::Error::new_spanned(
            &inputs[0],
            "First parameter must be '&mut self'",
        ));
    }

    // Extract message type from second parameter
    let message_type = match &inputs[1] {
        FnArg::Typed(PatType { ty, .. }) => ty,
        _ => {
            return Err(syn::Error::new_spanned(
                &inputs[1],
                "Second parameter must be a typed message parameter (e.g., 'msg: MessageType')",
            ))
        }
    };

    // Validate third parameter (&ActorRef<Self>)
    let third_param_valid = match &inputs[2] {
        FnArg::Typed(PatType { ty, .. }) => {
            match ty.as_ref() {
                Type::Reference(type_ref) => {
                    // Check if the referenced type contains "ActorRef" in its path
                    if let Type::Path(type_path) = type_ref.elem.as_ref() {
                        type_path
                            .path
                            .segments
                            .iter()
                            .any(|seg| seg.ident == "ActorRef")
                    } else {
                        false
                    }
                }
                _ => false,
            }
        }
        _ => false,
    };

    if !third_param_valid {
        return Err(syn::Error::new_spanned(
            &inputs[2],
            "Third parameter must be '&ActorRef<Self>' or '&rsactor::ActorRef<Self>'",
        ));
    }

    // Extract return type
    let return_type_ty = match &method.sig.output {
        ReturnType::Type(_, ty) => Some(ty.as_ref()),
        ReturnType::Default => None,
    };

    let return_type = match return_type_ty {
        Some(ty) => quote! { #ty },
        None => quote! { () },
    };

    // Determine whether to generate on_tell_result override
    let is_result = return_type_ty.map(is_result_type).unwrap_or(false);

    let should_generate_on_tell_result = if options.no_log {
        false
    } else if options.force_result {
        if return_type_ty.is_none() {
            return Err(syn::Error::new_spanned(
                &method.sig,
                "`#[handler(result)]` requires a return type, but this method returns `()`",
            ));
        }
        true
    } else {
        is_result
    };

    // Get method name
    let method_name = &method.sig.ident;

    let (impl_generics, _ty_generics, where_clause) = generics.split_for_impl();

    let on_tell_result_impl = if should_generate_on_tell_result {
        quote! {
            fn on_tell_result(result: &Self::Reply, actor_ref: &rsactor::ActorRef<Self>) {
                if let Err(ref e) = result {
                    rsactor::__log_handler_error(
                        &actor_ref.identity(),
                        std::any::type_name::<#message_type>(),
                        e
                    );
                }
            }
        }
    } else {
        quote! {}
    };

    // Generate the Message trait implementation
    let impl_tokens = quote! {
        impl #impl_generics rsactor::Message<#message_type> for #actor_type #where_clause {
            type Reply = #return_type;

            async fn handle(
                &mut self,
                msg: #message_type,
                actor_ref: &rsactor::ActorRef<Self>,
            ) -> Self::Reply {
                self.#method_name(msg, actor_ref).await
            }

            #on_tell_result_impl
        }
    };

    Ok(impl_tokens)
}

// TODO: Future enhancements that could be added:
//
// 1. Support for custom error types in derive macro:
//    #[derive(Actor)]
//    #[actor(error = "MyCustomError")]
//    struct MyActor { ... }
//
// 2. Support for custom Args types:
//    #[derive(Actor)]
//    #[actor(args = "MyArgsType")]
//    struct MyActor { ... }
//
// 3. Handler attribute with options:
//    #[handler(timeout = "5s")]
//    #[handler(priority = "high")]
//    async fn handle_message(&mut self, msg: Msg, _: &ActorRef<Self>) -> Reply
//
// 4. Automatic message struct generation:
//    #[message_handlers]
//    impl MyActor {
//        #[handler]
//        #[message(name = "Increment")]  // Generates struct Increment;
//        async fn handle_increment(&mut self, _: (), _: &ActorRef<Self>) -> u32
//    }
//
// 5. Validation attributes:
//    #[handler]
//    #[validate(non_empty, range(1..100))]
//    async fn handle_set_value(&mut self, msg: SetValue, _: &ActorRef<Self>) -> Result<(), Error>