From ccd4e5f76f12e5d409c6dcf7e03401391e06b550 Mon Sep 17 00:00:00 2001 From: elenaf9 <57632201+elenaf9@users.noreply.github.com> Date: Sat, 9 Jan 2021 14:28:36 +0100 Subject: [PATCH] Feat/messages with generics (#155) * Allow generics in actor macro attributes * test generic actor message * actor macro: handle fully qualified message paths --- riker-macros/src/lib.rs | 38 +++++++---- riker-macros/tests/macro.rs | 127 ++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 13 deletions(-) diff --git a/riker-macros/src/lib.rs b/riker-macros/src/lib.rs index 11c391ad..90cdd5ab 100644 --- a/riker-macros/src/lib.rs +++ b/riker-macros/src/lib.rs @@ -4,7 +4,9 @@ use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; -use syn::{DeriveInput, Generics}; +use syn::spanned::Spanned; +use syn::token::{Colon2, Comma}; +use syn::{DeriveInput, Generics, PathSegment, TypePath}; struct MsgTypes { types: Vec, @@ -12,7 +14,7 @@ struct MsgTypes { struct MsgVariant { name: Ident, - mtype: Ident, + mtype: TypePath, } impl MsgTypes { @@ -35,13 +37,13 @@ impl MsgTypes { impl Parse for MsgTypes { fn parse(input: ParseStream) -> Result { - let vars = Punctuated::::parse_terminated(input)?; + let vars = Punctuated::::parse_terminated(input)?; Ok(MsgTypes { types: vars .into_iter() .map(|t| MsgVariant { - name: get_name(&t), + name: get_name(&t.path.segments), mtype: t, }) .collect::>(), @@ -49,14 +51,24 @@ impl Parse for MsgTypes { } } -fn get_name(ident: &Ident) -> Ident { - let mut vname = format!("{}", ident.clone()); - - if let Some(c) = vname.get_mut(0..1) { - c.make_ascii_uppercase(); - } - - syn::Ident::new(&vname, ident.span()) +fn get_name(segments: &Punctuated) -> Ident { + let vname = segments + .iter() + .map(|seg| { + let ident = format!("{}", seg.ident); + ident + .split('_') + .map(|s| { + let mut s = s.to_string(); + if let Some(c) = s.get_mut(0..1) { + c.make_ascii_uppercase(); + } + s + }) + .collect::() + }) + .collect::(); + syn::Ident::new(&vname, segments.span()) } #[proc_macro_attribute] @@ -123,7 +135,7 @@ fn receive(aname: &Ident, gen: &Generics, name: &Ident, types: &MsgTypes) -> Tok } } -fn impl_into(name: &Ident, vname: &Ident, ty: &Ident) -> TokenStream { +fn impl_into(name: &Ident, vname: &Ident, ty: &TypePath) -> TokenStream { quote! { impl Into<#name> for #ty { fn into(self) -> #name { diff --git a/riker-macros/tests/macro.rs b/riker-macros/tests/macro.rs index db093c9f..4f5b4966 100644 --- a/riker-macros/tests/macro.rs +++ b/riker-macros/tests/macro.rs @@ -95,3 +95,130 @@ fn run_derived_generic_actor() { std::thread::sleep(std::time::Duration::from_millis(50)); } } + +#[derive(Clone, Debug)] +pub struct Message { + inner: T, +} + +#[actor(Message)] +#[derive(Clone, Default)] +struct GenericMsgActor; + +impl Actor for GenericMsgActor { + type Msg = GenericMsgActorMsg; + + fn supervisor_strategy(&self) -> Strategy { + Strategy::Stop + } + + fn recv(&mut self, ctx: &Context, msg: Self::Msg, sender: Sender) { + self.receive(ctx, msg, sender); + ctx.stop(&ctx.myself); + } +} + +impl Receive> for GenericMsgActor { + type Msg = GenericMsgActorMsg; + + fn receive( + &mut self, + _ctx: &Context, + msg: Message, + _sender: Option, + ) { + println!("{}", msg.inner); + } +} + +#[test] +fn run_generic_message_actor() { + let sys = ActorSystem::new().unwrap(); + + let act = sys.actor_of::("act").unwrap(); + + let msg = GenericMsgActorMsg::Message(Message { + inner: "test".to_string(), + }); + act.tell(msg, None); + + // wait until all direct children of the user root are terminated + while sys.user_root().has_children() { + // in order to lower cpu usage, sleep here + std::thread::sleep(std::time::Duration::from_millis(50)); + } +} + +mod test_mod { + #[derive(Clone, Debug)] + pub struct GenericMessage { + pub inner: T, + } + + #[derive(Clone, Debug)] + pub struct Message; +} + +#[actor(test_mod::GenericMessage, test_mod::Message)] +#[derive(Clone, Default)] +struct PathMsgActor; + +impl Actor for PathMsgActor { + type Msg = PathMsgActorMsg; + + fn supervisor_strategy(&self) -> Strategy { + Strategy::Stop + } + + fn recv(&mut self, ctx: &Context, msg: Self::Msg, sender: Sender) { + self.receive(ctx, msg, sender); + ctx.stop(&ctx.myself); + } +} + +impl Receive> for PathMsgActor { + type Msg = PathMsgActorMsg; + + fn receive( + &mut self, + _ctx: &Context, + msg: test_mod::GenericMessage, + _sender: Option, + ) { + println!("{}", msg.inner); + } +} + +impl Receive for PathMsgActor { + type Msg = PathMsgActorMsg; + + fn receive( + &mut self, + _ctx: &Context, + _msg: test_mod::Message, + _sender: Option, + ) { + println!("message"); + } +} + +#[test] +fn run_path_message_actor() { + let sys = ActorSystem::new().unwrap(); + + let act = sys.actor_of::("act").unwrap(); + + let msg = PathMsgActorMsg::TestModMessage(test_mod::Message); + act.tell(msg, None); + + let generic_msg = PathMsgActorMsg::TestModGenericMessage(test_mod::GenericMessage { + inner: "test".to_string(), + }); + act.tell(generic_msg, None); + + // wait until all direct children of the user root are terminated + while sys.user_root().has_children() { + // in order to lower cpu usage, sleep here + std::thread::sleep(std::time::Duration::from_millis(50)); + } +}