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
#![cfg_attr(doc, allow(unused_braces))]

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::*;

mod entangle;

/// The main item of the crate. This is a proc macro used as an attribute on the actor struct
/// definition, `Actor` implementation, and on an impl block in which the handler functions are used.
///
/// ## Example
/// ```rust,ignore
/// #![feature(type_alias_impl_trait, generic_associated_types)]
///
/// #[spaad::entangled]
/// struct Printer {
///     times: usize,
/// }
///
/// #[spaad::entangled]
/// impl Actor for Printer {
///     fn started(&mut self, _ctx: &mut Context<Self>) {
///         println!("Actor started!");
///     }
/// }
///
/// #[spaad::entangled]
/// impl Printer {
///     #[spaad::spawn]
///     fn new() -> Printer {
///         Printer { times: 0 }
///     }
///
///     #[spaad::handler]
///     async fn print(&mut self, string: String) {
///         self.times += 1;
///         println!("Printing {}. Printed {} times so far.", string, self.times);
///     }
/// }
/// ```
///
/// ## Constructors
/// To emit a constructor for an actor, the `#[spaad::spawn]` or `#[spaad::create]` attributes can
/// be used. The `spawn` macro will emit a method that constructs the actor with the given arguments
/// and spawns it onto whichever runtime `spaad` is set to use (currently, tokio, async_std, or
/// wasm-bindgen at your option). It is analagous to `Actor::spawn`. Similarly, `create` will
/// construct the actor, and return the address of the actor and its manager, ready to be spawned
/// onto any runtime. You can even have both on one function with `rename`:
///
/// ```rust,ignore
/// #[spaad::spawn]
/// #[spaad::create(rename = "create")]
/// fn new(x: u32) -> MyActor {
///     MyActor { x }
/// }
/// ```
///
/// This will cause a `create` function to be emitted, as well as a a spawn function named `new`.
///
/// ## Sending Messages
/// Messages can then be sent to actors as such:
/// ```rust,ignore
/// my_actor.print().await;
/// ```
///
/// The output type of the future will be determined by the signature. It will be identical to the
/// written type, except when the return is written as `Result<T, xtra::Disconnected>` (see below).
///
/// If you do not want to `await` for the message to complete processing, you can do the following:
/// ```rust,ignore
/// let _ = my_actor.print(); // Binding to avoid #[must_use] warning on Future
/// ```
///
/// This will also mean that the return type will be discarded, as the receiving end of the channel
/// will be dropped.
///
/// ## Handling disconnection
/// The methods to send messages will panic if the actor is disconnected. If you want to manually
/// handle this error, make the return type of the handler function `Result<T, xtra::Disconnected>`.
/// The type must be named `Context` - it cannot be renamed by re-importing. If you want to access
/// the actor cotnext add an argument to the function with `&mut Context<Self>` as the type.
/// Similarly, the type must be named `Context` - it cannot be renamed by re-importing.
///
/// ## Implementations in other modules
/// To implement something on an actor in a module other than where it is declared, you will need
/// to refer to it either by its fully-qualified path (e.g `crate::actor::MyActor`) or a local path
/// (e.g `super::MyActor`) when writing the self-type. So, instead of writing this:
///
/// ```rust,ignore
/// use super::MyActor;
/// #[spaad::entangled]
/// impl AsRef<i32> for MyActor { /* ... */ }
/// ```
///
/// you must write this:
///
/// ```rust,ignore
/// #[spaad::entangled]
/// impl AsRef<i32> for super::MyActor { /* ... */ }
/// ```
///
/// This is a limitation due to how the macro expands, and should be resolved when there is support
/// for inherent-impl type aliases (see [rust/60471](https://github.com/rust-lang/rfcs/issues/1697)).
/// This is currently blocked on lazy normalization.
#[proc_macro_error::proc_macro_error]
#[proc_macro_attribute]
pub fn entangled(_args: TokenStream, input: TokenStream) -> TokenStream {
    entangle::entangle(input)
}

// The below attributes are just markers, so they just strip themselves from the output and output
// the rest of the function.

/// This marks a function as a handler for a type of message, exposing it to external callers.
///
/// ## Usage
///
/// ```ignore
/// #[spaad::handler]
/// async fn do_something(&mut self) {/* ... */}
///
/// // will generate a message for you
/// #[spaad::handler]
/// async fn do_something(&mut self, str: String) {/* ... */}
///
/// // will reuse an existing message
/// #[spaad::handler(msg = "AMsg")]
/// async fn do_something_with_a_msg(&mut self, msg: AMsg) {/* ... */}
/// ```
#[proc_macro_attribute]
pub fn handler(_args: TokenStream, input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as ImplItemMethod);
    TokenStream::from(quote!(#input))
}

/// This marks a function as the method that should be used to create and spawn the actor. It must
/// return the type of the actor, either as `Self` or by its name.
///
/// ## Arguments
///
/// This macro can be passed what to rename the method in the form of `rename = "{new name}"`.
/// This is most useful when generating both a create and spawn method from the same constructor.
///
/// ## Usage
///
/// ```ignore
/// #[spaad::spawn]
/// fn new(some: Thing) -> MyActor {
///     MyActor { some }
/// }
///
/// #[spaad::spawn(rename = "something_else")] // Emits a method called `something_else`
///  fn spawn2(some: Thing) -> MyActor {
///      MyActor { some }
///  }
/// ```
#[proc_macro_attribute]
pub fn spawn(_args: TokenStream, input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as ImplItemMethod);
    TokenStream::from(quote!(#input))
}

/// This marks a function as the method that should be used to create the actor. It emits a method
/// which returns a tuple of the address to the actor and its `ActorManager`.
///
/// ## Arguments
///
/// This macro can be passed what to rename the method in the form of `rename = "{new name}"`.
/// This is most useful when generating both a create and spawn method from the same constructor.
///
/// ## Usage
///
/// ```ignore
/// #[spaad::create]
/// fn create(some: Thing) -> MyActor {
///     MyActor { some }
/// }
///
/// #[spaad::create(rename = "something_else")] // Emits a method called `something_else`
/// fn create2(some: Thing) -> MyActor {
///     MyActor { some }
/// }
/// ```
#[proc_macro_attribute]
pub fn create(_args: TokenStream, input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as ImplItemMethod);
    TokenStream::from(quote!(#input))
}