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
//! # 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; /// # 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 { match act_zero_impl(item) { Ok(tokens) => tokens, Err(e) => e.to_compile_error(), } .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", )) } }) }