Browse Source
407: Xtra productivity hell yeah r=thomaseizinger a=da-kami - [x] Optimize message block generation - [x] Use the actual variable names that were used in the function sig in the generated code - [x] Get the xtra::Context into the macro (third argument that can be specified on function sig) Co-authored-by: Daniel Karzel <daniel@comit.network> Co-authored-by: Mariusz Klochowicz <mariusz@klochowicz.com>feature/actor-custom-derive
bors[bot]
3 years ago
committed by
GitHub
9 changed files with 228 additions and 98 deletions
@ -0,0 +1,17 @@ |
|||||
|
[package] |
||||
|
name = "xtra_productivity" |
||||
|
version = "0.1.0" |
||||
|
edition = "2018" |
||||
|
|
||||
|
[lib] |
||||
|
proc-macro = true |
||||
|
|
||||
|
[dependencies] |
||||
|
quote = "1" |
||||
|
syn = { version = "1", features = ["full"] } |
||||
|
|
||||
|
[dev-dependencies] |
||||
|
async-trait = "0.1.51" |
||||
|
tokio = { version = "1", features = ["rt-multi-thread", "macros"] } |
||||
|
trybuild = "1" |
||||
|
xtra = { version = "0.6", features = ["with-tokio-1"] } |
@ -0,0 +1,55 @@ |
|||||
|
use proc_macro::TokenStream; |
||||
|
use quote::quote; |
||||
|
use syn::{FnArg, ImplItem, ItemImpl, ReturnType}; |
||||
|
|
||||
|
#[proc_macro_attribute] |
||||
|
pub fn xtra_productivity(_attribute: TokenStream, item: TokenStream) -> TokenStream { |
||||
|
let block = syn::parse::<ItemImpl>(item).unwrap(); |
||||
|
|
||||
|
let actor = block.self_ty; |
||||
|
|
||||
|
let code = block |
||||
|
.items |
||||
|
.into_iter() |
||||
|
.filter_map(|block_item| match block_item { |
||||
|
ImplItem::Method(method) => Some(method), |
||||
|
_ => None, |
||||
|
}) |
||||
|
.map(|method| { |
||||
|
let message_arg = method.sig.inputs[1].clone(); |
||||
|
|
||||
|
let message_type = match message_arg { |
||||
|
// receiver represents self
|
||||
|
FnArg::Receiver(_) => unreachable!("cannot have receiver on second position"), |
||||
|
FnArg::Typed(ref typed) => typed.ty.clone() |
||||
|
}; |
||||
|
|
||||
|
let method_return = method.sig.output; |
||||
|
let method_block = method.block; |
||||
|
|
||||
|
let context_arg = method.sig.inputs.iter().nth(2).map(|fn_arg| quote! { #fn_arg }).unwrap_or_else(|| quote! { |
||||
|
_ctx: &mut xtra::Context<Self> |
||||
|
}); |
||||
|
|
||||
|
let result_type = match method_return { |
||||
|
ReturnType::Default => quote! { () }, |
||||
|
ReturnType::Type(_, ref t) => quote! { #t } |
||||
|
}; |
||||
|
|
||||
|
quote! { |
||||
|
impl xtra::Message for #message_type { |
||||
|
type Result = #result_type; |
||||
|
} |
||||
|
|
||||
|
#[async_trait] |
||||
|
impl xtra::Handler<#message_type> for #actor { |
||||
|
async fn handle(&mut self, #message_arg, #context_arg) #method_return #method_block |
||||
|
} |
||||
|
} |
||||
|
}).collect::<Vec<_>>(); |
||||
|
|
||||
|
(quote! { |
||||
|
#(#code)* |
||||
|
}) |
||||
|
.into() |
||||
|
} |
@ -0,0 +1,43 @@ |
|||||
|
use async_trait::async_trait; |
||||
|
use xtra::spawn::TokioGlobalSpawnExt; |
||||
|
use xtra::Actor; |
||||
|
use xtra_productivity::xtra_productivity; |
||||
|
|
||||
|
struct DummyActor; |
||||
|
|
||||
|
impl xtra::Actor for DummyActor {} |
||||
|
|
||||
|
#[derive(Clone)] |
||||
|
struct DummyMessage; |
||||
|
|
||||
|
struct DummyMessageWithContext; |
||||
|
|
||||
|
// Dummy actor, xtra::Handler and xtra::Message impls generated by xtra_productivity
|
||||
|
#[xtra_productivity] |
||||
|
impl DummyActor { |
||||
|
pub fn handle_dummy_message(&mut self, message: DummyMessage) -> i32 { |
||||
|
let _ = message.clone(); |
||||
|
0 |
||||
|
} |
||||
|
|
||||
|
pub fn handle_dummy_message_with_context( |
||||
|
&mut self, |
||||
|
_message: DummyMessageWithContext, |
||||
|
context: &mut xtra::Context<Self>, |
||||
|
) { |
||||
|
let _ = context.address(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn is_i32(_: i32) {} |
||||
|
|
||||
|
#[tokio::main] |
||||
|
async fn main() { |
||||
|
// Create dummy actor
|
||||
|
let dummy_actor = DummyActor.create(None).spawn_global(); |
||||
|
|
||||
|
// Send message to dummy actor
|
||||
|
let i32 = dummy_actor.send(DummyMessage).await.unwrap(); |
||||
|
is_i32(i32); |
||||
|
dummy_actor.send(DummyMessageWithContext).await.unwrap(); |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
#[test] |
||||
|
fn ui() { |
||||
|
let t = trybuild::TestCases::new(); |
||||
|
t.compile_fail("tests/ui/*.rs"); |
||||
|
t.pass("tests/pass/*.rs"); |
||||
|
} |
Loading…
Reference in new issue