Mariusz Klochowicz
3 years ago
8 changed files with 4 additions and 239 deletions
@ -1,17 +0,0 @@ |
|||
[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"] } |
@ -1,107 +0,0 @@ |
|||
use proc_macro::TokenStream; |
|||
use quote::quote; |
|||
use syn::{FnArg, GenericParam, ImplItem, ItemImpl, MetaNameValue, ReturnType}; |
|||
|
|||
#[proc_macro_attribute] |
|||
pub fn xtra_productivity(attribute: TokenStream, item: TokenStream) -> TokenStream { |
|||
let block = syn::parse::<ItemImpl>(item).unwrap(); |
|||
let want_message_impl = if attribute.is_empty() { |
|||
true |
|||
} else { |
|||
let attribute = syn::parse::<MetaNameValue>(attribute).unwrap(); |
|||
if !attribute.path.is_ident("message_impl") { |
|||
panic!( |
|||
"Unexpected attribute {:?}", |
|||
attribute.path.get_ident().unwrap() |
|||
) |
|||
} |
|||
|
|||
matches!( |
|||
attribute.lit, |
|||
syn::Lit::Bool(syn::LitBool { value: true, .. }) |
|||
) |
|||
}; |
|||
|
|||
let actor = block.self_ty; |
|||
|
|||
let generic_params = &block.generics.params; |
|||
|
|||
let generic_types = block |
|||
.generics |
|||
.params |
|||
.iter() |
|||
.filter_map(|param| match param { |
|||
GenericParam::Type(ty) => Some(ty.ident.clone()), |
|||
_ => None, |
|||
}) |
|||
.collect::<Vec<_>>(); |
|||
|
|||
let additional_bounds = block |
|||
.generics |
|||
.where_clause |
|||
.map(|bounds| { |
|||
let predicates = bounds.predicates; |
|||
|
|||
quote! { |
|||
#predicates |
|||
} |
|||
}) |
|||
.unwrap_or_default(); |
|||
|
|||
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 } |
|||
}; |
|||
|
|||
let message_impl = if want_message_impl { |
|||
quote! { |
|||
impl xtra::Message for #message_type { |
|||
type Result = #result_type; |
|||
} |
|||
} |
|||
} else { |
|||
quote! {} |
|||
}; |
|||
|
|||
quote! { |
|||
#message_impl |
|||
|
|||
#[async_trait::async_trait] |
|||
impl<#generic_params> xtra::Handler<#message_type> for #actor |
|||
where |
|||
#additional_bounds |
|||
#(#generic_types: Send + 'static),* |
|||
{ |
|||
async fn handle(&mut self, #message_arg, #context_arg) #method_return #method_block |
|||
} |
|||
} |
|||
}).collect::<Vec<_>>(); |
|||
|
|||
(quote! { |
|||
#(#code)* |
|||
}) |
|||
.into() |
|||
} |
@ -1,27 +0,0 @@ |
|||
use std::marker::PhantomData; |
|||
use xtra_productivity::xtra_productivity; |
|||
|
|||
struct ActorWithParam<C> { |
|||
ty: PhantomData<C>, |
|||
} |
|||
|
|||
struct DummyMessage; |
|||
|
|||
trait Foo {} |
|||
|
|||
impl<C: 'static + Send> xtra::Actor for ActorWithParam<C> {} |
|||
|
|||
// Dummy actor, xtra::Handler and xtra::Message impls generated by xtra_productivity
|
|||
#[xtra_productivity] |
|||
impl<C> ActorWithParam<C> |
|||
where |
|||
C: Foo, |
|||
{ |
|||
pub fn handle_dummy_message(&mut self, _message: DummyMessage) { |
|||
assert_impls_foo::<C>(); |
|||
} |
|||
} |
|||
|
|||
fn assert_impls_foo<T: Foo>() {} |
|||
|
|||
fn main() {} |
@ -1,62 +0,0 @@ |
|||
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) {} |
|||
|
|||
struct DummyMessageWithoutMessageImpl; |
|||
|
|||
#[xtra_productivity(message_impl = false)] |
|||
impl DummyActor { |
|||
pub fn handle_dummy_message_without_message_impl( |
|||
&mut self, |
|||
_message: DummyMessageWithoutMessageImpl, |
|||
) { |
|||
} |
|||
} |
|||
|
|||
impl xtra::Message for DummyMessageWithoutMessageImpl { |
|||
type Result = (); |
|||
} |
|||
|
|||
#[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(); |
|||
|
|||
dummy_actor |
|||
.send(DummyMessageWithoutMessageImpl) |
|||
.await |
|||
.unwrap(); |
|||
} |
@ -1,6 +0,0 @@ |
|||
#[test] |
|||
fn ui() { |
|||
let t = trybuild::TestCases::new(); |
|||
t.compile_fail("tests/ui/*.rs"); |
|||
t.pass("tests/pass/*.rs"); |
|||
} |
Loading…
Reference in new issue