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
//! # act-zero-macro
//!
//! Procedural macros for the `act-zero` crate.
#![deny(missing_docs)]

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;

mod common;
mod expand_impl;
mod expand_trait;
mod receiver;
mod respan;

/// # The `#[act_zero]` macro.
///
/// This macro is used in two locations, which will be documented separately:
///
/// ## Trait position
///
/// ```nocompile
/// #[act_zero]
/// trait Greeter {
///     fn greet(&self, name: String, res: Sender<String>);
/// }
/// ```
///
/// This is used to define an *actor trait*. Actor traits (much like normal traits) define a
/// set of methods to be implemented by the actor type. However, there are a number of
/// constraints:
/// - Methods must take `&self` as the receiver.
/// - All argument types must be `Send + 'static`.
/// - Generic methods must have a `where Self: Sized` bound.
/// - There must be no return type.
/// - If the last argument is named `res`, it must be of type `Sender<_>`.
///
/// These last two constraints are related: actor systems work by passing messages and
/// processing them asynchronously. Any return value would not be immediately available,
/// so instead the caller passes in a `Sender<_>` to receive the result.
///
/// As a convenience, there will be an asynchronous `call_xyz()` method to hide this
/// complexity from the caller:
/// ```nocompile
/// let addr: Addr<dyn Greeter> = ...;
/// let res = addr.call_greet("John".into()).await?;
/// ```
///
/// Internally, this macro defines several additional items:
/// - `trait GreeterImpl`
///   You will never use this trait directly, but if you want your trait to be implementable
///   by downstream crates, you must export this trait under the same path as the actor trait
///   itself.
/// - `enum GreeterMsg`
///   This enum contains a variant for each object-safe method in the trait. It is used in
///   remoting scenarios when messages may need to be trasferred between processes or over
///   the network, but is otherwise not used.
/// - `trait GreeterExt`
///   This trait is implemented for the `Addr` and `WeakAddr` types when they contain a type
///   implementing the actor trait. Methods on this trait are the primary way you would
///   interact with the actor, and this is where helper methods like `call_greeter` are
///   defined.
///
/// ## Trait implementation position
///
/// ```nocompile
/// #[act_zero]
/// impl Greeter for SimpleGreeter {
///     async fn greet(&mut self, name: String, res: Sender<String>) {
///         self.number_of_greets += 1;
///         res.send(format!(
///             "Hello, {}. You are number {}!",
///             name, self.number_of_greets
///         ))
///         .ok();
///     }
/// }
/// ```
///
/// This is used to implement an actor trait for an actor. The signature of the methods
/// in the implementation differ substantially from those signature in the trait definition:
///
/// - All methods must be `async`. This may be optional in future.
/// - Methods can return any type implementing `IntoResult`. If an error variant is returned
///   it will be passed to the appropriate error handler implemented for the actor.
/// - The receiver type can be any of:
///
///   1) `&mut self`
///      The method will run to completion with exclusive access to the actor state.
///      No methods taking `&self` or `&mut self` can run concurrently. Long running
///      methods taking `&mut self` should be avoided if you want your actor to stay
///      responsive to new messages.
///
///   2) `&self`
///      The method will run to completion with shared access to the actor state.
///      Other methods taking `&self` can run concurrently, but methods taking
///      `&mut self` cannot.
///
///   3) `self: Addr<Local<Self>>`
///      The method has no direct access to the actor state, but can run concurrently
///      with any other methods, including those taking `&mut self`. This can be useful
///      for implementing delayed or timed events without blocking the actor.
///      Access to the actor state can be achieved by awaiting another method on the
///      actor to do the update.
///
/// No additional items are generated by the expansion of this variant of the macro.
#[proc_macro_attribute]
pub fn act_zero(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let res = match act_zero_impl(item) {
        Ok(tokens) => tokens,
        Err(e) => e.to_compile_error(),
    };
    res.into()
}

fn act_zero_impl(item: TokenStream) -> syn::Result<TokenStream2> {
    let item: syn::Item = syn::parse(item)?;
    Ok(match item {
        syn::Item::Trait(trait_item) => expand_trait::expand(trait_item)?,
        syn::Item::Impl(impl_item) => expand_impl::expand(impl_item)?,
        _ => {
            return Err(syn::Error::new_spanned(
                item,
                "Expected a trait or a trait implementation",
            ))
        }
    })
}