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