actix_handler_macro/lib.rs
1//! # actix_handler_macro
2//! Helper macros for using Actix.
3//!
4//! ```rust
5//! use actix::Message;
6//! use actix_handler_macro::{actix_handler, Actor};
7//!
8//! #[derive(Actor)]
9//! struct Example;
10//! type ExampleContext = actix::Context<Example>;
11//!
12//! #[derive(Message)]
13//! #[rtype(result = "String")]
14//! struct Greeting {
15//! name: String,
16//! }
17//!
18//! #[actix_handler]
19//! impl Example {
20//! fn greet(&self, message: Greeting, _ctx: &ExampleContext) -> String {
21//! format!("Hello {}", message.name).to_string()
22//! }
23//! }
24//! ```
25mod actor_derive;
26mod expand_addr;
27mod expand_impl_handlers;
28mod expand_method_handlers;
29mod options;
30mod utils;
31
32use expand_impl_handlers::expand_item_impl;
33use options::{parse_options, Options};
34use proc_macro::TokenStream;
35use quote::quote;
36use syn::{parse_macro_input, AttributeArgs, DeriveInput, Item};
37use utils::compilation_error;
38
39/// Allows writing Actix actors with impl blocks.
40///
41/// ## Motivation
42/// Actix can be quite verbose when declaring handlers:
43///
44/// ```rust
45/// use actix::Actor;
46/// use actix::Context;
47/// use actix::Handler;
48/// use actix::Message;
49///
50/// struct Example;
51///
52/// impl Actor for Example {
53/// type Context = Context<Example>;
54/// }
55///
56/// #[derive(Message)]
57/// #[rtype(result = "String")]
58/// struct Greeting { name: String }
59///
60/// impl Handler<Greeting> for Example {
61/// type Result = String;
62///
63/// fn handle(&mut self, msg: Greeting, ctx: &mut Context<Self>) -> Self::Result {
64/// unimplemented!()
65/// }
66/// }
67/// ```
68///
69/// `actix_derive_macro` reads an `impl` block and generates a bit of this code for each method.
70///
71/// ## Usage
72/// ```rust
73/// use actix_handler_macro::{Actor, actix_handler};
74/// use actix::{Actor, Addr, Context, Message, System};
75///
76///
77/// #[derive(Actor)]
78/// struct Example;
79///
80/// type ExampleContext = Context<Example>;
81///
82/// #[derive(Clone, Message)]
83/// #[rtype(result = "String")]
84/// struct Greeting { name: String }
85///
86/// #[actix_handler]
87/// impl Example {
88/// fn greet(&self, message: Greeting, _ctx: &ExampleContext) -> String {
89/// format!("Hello {}", message.name).to_string()
90/// }
91/// }
92///
93/// fn example_usage() {
94/// let mut sys = System::new("actix-test-runtime");
95/// let addr: Addr<Example> = Example {}.start();
96///
97/// sys.block_on(async move {
98/// let greeting = Greeting { name: "you".to_string() };
99/// // `Example` has its handler impl block
100/// let result = addr.send(greeting.clone()).await.ok().unwrap();
101/// assert_eq!(result, "Hello you");
102///
103/// // An Addr trait is also expanded:
104/// let result = addr.greet(greeting.clone()).await.ok().unwrap();
105/// assert_eq!(result, "Hello you")
106/// });
107/// }
108/// ```
109///
110/// This will expand a `Handler<Greeting>` impl for each method in Example.
111///
112/// ## Actor `...Addr` trait
113/// It'll also output a trait `GreetingAddr` and its implementation for `Addr<Example>` with
114/// convenience methods:
115///
116/// ```ignore
117/// // Example output
118/// trait GreetingAddr {
119/// fn greet(self: &Self, msg: Greeting) -> actix::prelude::Request<Example, Greeting>;
120/// }
121/// ```
122///
123/// ## RecipientRequest
124///
125/// Optionally, the trait can use a `actix::Recipient` and return a `actix::RecipientRequest`.
126///
127/// ```rust
128/// use actix::{Message};
129/// use actix_handler_macro::{actix_handler, Actor};
130///
131/// #[derive(Actor)]
132/// struct Example;
133/// type ExampleContext = actix::Context<Example>;
134/// #[derive(Message)]
135/// #[rtype(result = "String")]
136/// struct Greeting { name: String }
137///
138/// #[actix_handler(use_recipient)]
139/// impl Example {
140/// fn greet(&self, message: Greeting, _ctx: &ExampleContext) -> String {
141/// format!("Hello {}", message.name).to_string()
142/// }
143/// }
144/// ```
145///
146/// `#[actix_handler(use_recipient)]` will expand the `GreetingAddr` trait as:
147///
148/// ```skip
149/// // Example output
150/// trait GreetingAddr {
151/// fn greet(self: &Self, msg: Greeting) -> actix::RecipientRequest<Greeting>;
152/// }
153/// ```
154///
155/// A mock of the actor could be implemented as:
156/// ```skip
157/// use actix::Message;
158/// use actix_handler_macro::Actor;
159///
160/// #[derive(Actor)]
161/// struct Example;
162///
163/// #[derive(Message)]
164/// #[rtype(result = "String")]
165/// struct Greeting;
166///
167/// #[derive(Actor)]
168/// struct ExampleMock {
169/// mocker: actix::Addr<actix::actors::mocker::Mocker<Example>>,
170/// }
171///
172/// impl GreetingAddr for ExampleMock {
173/// fn greet(self: &Self, msg: Greeting) -> actix::prelude::RecipientRequest<Greeting> {
174/// self.mocker.clone().recipient().send(msg)
175/// }
176/// }
177/// ```
178#[proc_macro_attribute]
179pub fn actix_handler(args: TokenStream, input: TokenStream) -> TokenStream {
180 let parsed_args = parse_macro_input!(args as AttributeArgs);
181 let options = parse_options(parsed_args);
182
183 let mut parsed_input = parse_macro_input!(input as Item);
184 let expanded = expand_actix_handler(options, &mut parsed_input);
185
186 let mut output = TokenStream::from(quote!(#parsed_input));
187 output.extend(expanded);
188 output
189}
190
191fn expand_actix_handler(options: Options, input: &mut Item) -> TokenStream {
192 match input {
193 Item::Impl(item_impl) => expand_item_impl(options, item_impl),
194 _ => compilation_error("#[actix_handler] will only work on 'impl' blocks"),
195 }
196}
197
198#[proc_macro_derive(Actor, attributes(actor))]
199pub fn actor_derive(input: TokenStream) -> TokenStream {
200 let ast: DeriveInput = syn::parse(input).unwrap();
201 actor_derive::actor::expand(&ast).into()
202}
203
204#[proc_macro_derive(Supervised)]
205pub fn supervised_derive(input: TokenStream) -> TokenStream {
206 let ast: DeriveInput = syn::parse(input).unwrap();
207 actor_derive::supervised::expand(&ast).into()
208}
209
210#[proc_macro_derive(ArbiterService)]
211pub fn arbiter_service_derive(input: TokenStream) -> TokenStream {
212 let ast: DeriveInput = syn::parse(input).unwrap();
213 actor_derive::arbiter_service::expand(&ast).into()
214}