act_zero_macro/
lib.rs

1//! # act-zero-macro
2//!
3//! Procedural macros for the `act-zero` crate.
4#![deny(missing_docs)]
5
6use proc_macro::TokenStream;
7use proc_macro2::TokenStream as TokenStream2;
8
9mod common;
10mod expand_impl;
11mod expand_trait;
12mod receiver;
13mod respan;
14
15/// # The `#[act_zero]` macro.
16///
17/// This macro is used in two locations, which will be documented separately:
18///
19/// ## Trait position
20///
21/// ```nocompile
22/// #[act_zero]
23/// trait Greeter {
24///     fn greet(&self, name: String, res: Sender<String>);
25/// }
26/// ```
27///
28/// This is used to define an *actor trait*. Actor traits (much like normal traits) define a
29/// set of methods to be implemented by the actor type. However, there are a number of
30/// constraints:
31/// - Methods must take `&self` as the receiver.
32/// - All argument types must be `Send + 'static`.
33/// - Generic methods must have a `where Self: Sized` bound.
34/// - There must be no return type.
35/// - If the last argument is named `res`, it must be of type `Sender<_>`.
36///
37/// These last two constraints are related: actor systems work by passing messages and
38/// processing them asynchronously. Any return value would not be immediately available,
39/// so instead the caller passes in a `Sender<_>` to receive the result.
40///
41/// As a convenience, there will be an asynchronous `call_xyz()` method to hide this
42/// complexity from the caller:
43/// ```nocompile
44/// let addr: Addr<dyn Greeter> = ...;
45/// let res = addr.call_greet("John".into()).await?;
46/// ```
47///
48/// Internally, this macro defines several additional items:
49/// - `trait GreeterImpl`
50///   You will never use this trait directly, but if you want your trait to be implementable
51///   by downstream crates, you must export this trait under the same path as the actor trait
52///   itself.
53/// - `enum GreeterMsg`
54///   This enum contains a variant for each object-safe method in the trait. It is used in
55///   remoting scenarios when messages may need to be trasferred between processes or over
56///   the network, but is otherwise not used.
57/// - `trait GreeterExt`
58///   This trait is implemented for the `Addr` and `WeakAddr` types when they contain a type
59///   implementing the actor trait. Methods on this trait are the primary way you would
60///   interact with the actor, and this is where helper methods like `call_greeter` are
61///   defined.
62///
63/// ## Trait implementation position
64///
65/// ```nocompile
66/// #[act_zero]
67/// impl Greeter for SimpleGreeter {
68///     async fn greet(&mut self, name: String, res: Sender<String>) {
69///         self.number_of_greets += 1;
70///         res.send(format!(
71///             "Hello, {}. You are number {}!",
72///             name, self.number_of_greets
73///         ))
74///         .ok();
75///     }
76/// }
77/// ```
78///
79/// This is used to implement an actor trait for an actor. The signature of the methods
80/// in the implementation differ substantially from those signature in the trait definition:
81///
82/// - All methods must be `async`. This may be optional in future.
83/// - Methods can return any type implementing `IntoResult`. If an error variant is returned
84///   it will be passed to the appropriate error handler implemented for the actor.
85/// - The receiver type can be any of:
86///
87///   1) `&mut self`
88///      The method will run to completion with exclusive access to the actor state.
89///      No methods taking `&self` or `&mut self` can run concurrently. Long running
90///      methods taking `&mut self` should be avoided if you want your actor to stay
91///      responsive to new messages.
92///
93///   2) `&self`
94///      The method will run to completion with shared access to the actor state.
95///      Other methods taking `&self` can run concurrently, but methods taking
96///      `&mut self` cannot.
97///
98///   3) `self: Addr<Local<Self>>`
99///      The method has no direct access to the actor state, but can run concurrently
100///      with any other methods, including those taking `&mut self`. This can be useful
101///      for implementing delayed or timed events without blocking the actor.
102///      Access to the actor state can be achieved by awaiting another method on the
103///      actor to do the update.
104///
105/// No additional items are generated by the expansion of this variant of the macro.
106#[proc_macro_attribute]
107pub fn act_zero(_attr: TokenStream, item: TokenStream) -> TokenStream {
108    let res = match act_zero_impl(item) {
109        Ok(tokens) => tokens,
110        Err(e) => e.to_compile_error(),
111    };
112    res.into()
113}
114
115fn act_zero_impl(item: TokenStream) -> syn::Result<TokenStream2> {
116    let item: syn::Item = syn::parse(item)?;
117    Ok(match item {
118        syn::Item::Trait(trait_item) => expand_trait::expand(trait_item)?,
119        syn::Item::Impl(impl_item) => expand_impl::expand(impl_item)?,
120        _ => {
121            return Err(syn::Error::new_spanned(
122                item,
123                "Expected a trait or a trait implementation",
124            ))
125        }
126    })
127}