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
//! # actix_handler_macro //! Helper macros for using Actix. //! //! ```rust //! use actix::Message; //! use actix_handler_macro::{actix_handler, Actor}; //! //! #[derive(Actor)] //! struct Example; //! type ExampleContext = actix::Context<Example>; //! //! #[derive(Message)] //! #[rtype(result = "String")] //! struct Greeting { //! name: String, //! } //! //! #[actix_handler] //! impl Example { //! fn greet(&self, message: Greeting, _ctx: &ExampleContext) -> String { //! format!("Hello {}", message.name).to_string() //! } //! } //! ``` mod actor_derive; mod expand_addr; mod expand_impl_handlers; mod expand_method_handlers; mod options; mod utils; use expand_impl_handlers::expand_item_impl; use options::{parse_options, Options}; use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, AttributeArgs, DeriveInput, Item}; use utils::compilation_error; /// Allows writing Actix actors with impl blocks. /// /// ## Motivation /// Actix can be quite verbose when declaring handlers: /// /// ```rust /// use actix::Actor; /// use actix::Context; /// use actix::Handler; /// use actix::Message; /// /// struct Example; /// /// impl Actor for Example { /// type Context = Context<Example>; /// } /// /// #[derive(Message)] /// #[rtype(result = "String")] /// struct Greeting { name: String } /// /// impl Handler<Greeting> for Example { /// type Result = String; /// /// fn handle(&mut self, msg: Greeting, ctx: &mut Context<Self>) -> Self::Result { /// unimplemented!() /// } /// } /// ``` /// /// `actix_derive_macro` reads an `impl` block and generates a bit of this code for each method. /// /// ## Usage /// ```rust /// use actix_handler_macro::{Actor, actix_handler}; /// use actix::{Actor, Addr, Context, Message, System}; /// /// /// #[derive(Actor)] /// struct Example; /// /// type ExampleContext = Context<Example>; /// /// #[derive(Clone, Message)] /// #[rtype(result = "String")] /// struct Greeting { name: String } /// /// #[actix_handler] /// impl Example { /// fn greet(&self, message: Greeting, _ctx: &ExampleContext) -> String { /// format!("Hello {}", message.name).to_string() /// } /// } /// /// fn example_usage() { /// let mut sys = System::new("actix-test-runtime"); /// let addr: Addr<Example> = Example {}.start(); /// /// sys.block_on(async move { /// let greeting = Greeting { name: "you".to_string() }; /// // `Example` has its handler impl block /// let result = addr.send(greeting.clone()).await.ok().unwrap(); /// assert_eq!(result, "Hello you"); /// /// // An Addr trait is also expanded: /// let result = addr.greet(greeting.clone()).await.ok().unwrap(); /// assert_eq!(result, "Hello you") /// }); /// } /// ``` /// /// This will expand a `Handler<Greeting>` impl for each method in Example. /// /// ## Actor `...Addr` trait /// It'll also output a trait `GreetingAddr` and its implementation for `Addr<Example>` with /// convenience methods: /// /// ```ignore /// // Example output /// trait GreetingAddr { /// fn greet(self: &Self, msg: Greeting) -> actix::prelude::Request<Example, Greeting>; /// } /// ``` /// /// ## RecipientRequest /// /// Optionally, the trait can use a `actix::Recipient` and return a `actix::RecipientRequest`. /// /// ```rust /// use actix::{Message}; /// use actix_handler_macro::{actix_handler, Actor}; /// /// #[derive(Actor)] /// struct Example; /// type ExampleContext = actix::Context<Example>; /// #[derive(Message)] /// #[rtype(result = "String")] /// struct Greeting { name: String } /// /// #[actix_handler(use_recipient)] /// impl Example { /// fn greet(&self, message: Greeting, _ctx: &ExampleContext) -> String { /// format!("Hello {}", message.name).to_string() /// } /// } /// ``` /// /// `#[actix_handler(use_recipient)]` will expand the `GreetingAddr` trait as: /// /// ```skip /// // Example output /// trait GreetingAddr { /// fn greet(self: &Self, msg: Greeting) -> actix::RecipientRequest<Greeting>; /// } /// ``` /// /// A mock of the actor could be implemented as: /// ```skip /// use actix::Message; /// use actix_handler_macro::Actor; /// /// #[derive(Actor)] /// struct Example; /// /// #[derive(Message)] /// #[rtype(result = "String")] /// struct Greeting; /// /// #[derive(Actor)] /// struct ExampleMock { /// mocker: actix::Addr<actix::actors::mocker::Mocker<Example>>, /// } /// /// impl GreetingAddr for ExampleMock { /// fn greet(self: &Self, msg: Greeting) -> actix::prelude::RecipientRequest<Greeting> { /// self.mocker.clone().recipient().send(msg) /// } /// } /// ``` #[proc_macro_attribute] pub fn actix_handler(args: TokenStream, input: TokenStream) -> TokenStream { let parsed_args = parse_macro_input!(args as AttributeArgs); let options = parse_options(parsed_args); let mut parsed_input = parse_macro_input!(input as Item); let expanded = expand_actix_handler(options, &mut parsed_input); let mut output = TokenStream::from(quote!(#parsed_input)); output.extend(expanded); output } fn expand_actix_handler(options: Options, input: &mut Item) -> TokenStream { match input { Item::Impl(item_impl) => expand_item_impl(options, item_impl), _ => compilation_error("#[actix_handler] will only work on 'impl' blocks"), } } #[proc_macro_derive(Actor, attributes(actor))] pub fn actor_derive(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); actor_derive::actor::expand(&ast).into() } #[proc_macro_derive(Supervised)] pub fn supervised_derive(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); actor_derive::supervised::expand(&ast).into() } #[proc_macro_derive(ArbiterService)] pub fn arbiter_service_derive(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); actor_derive::arbiter_service::expand(&ast).into() }