Skip to content

Commit

Permalink
Feat/messages with generics (#155)
Browse files Browse the repository at this point in the history
* Allow generics in actor macro attributes

* test generic actor message

* actor macro: handle fully qualified message paths
  • Loading branch information
elenaf9 committed Jan 9, 2021
1 parent 641f627 commit ccd4e5f
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 13 deletions.
38 changes: 25 additions & 13 deletions riker-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ 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<MsgVariant>,
}

struct MsgVariant {
name: Ident,
mtype: Ident,
mtype: TypePath,
}

impl MsgTypes {
Expand All @@ -35,28 +37,38 @@ impl MsgTypes {

impl Parse for MsgTypes {
fn parse(input: ParseStream) -> Result<Self> {
let vars = Punctuated::<Ident, syn::token::Comma>::parse_terminated(input)?;
let vars = Punctuated::<TypePath, Comma>::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::<Vec<_>>(),
})
}
}

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<PathSegment, Colon2>) -> 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::<String>()
})
.collect::<String>();
syn::Ident::new(&vname, segments.span())
}

#[proc_macro_attribute]
Expand Down Expand Up @@ -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 {
Expand Down
127 changes: 127 additions & 0 deletions riker-macros/tests/macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,130 @@ fn run_derived_generic_actor() {
std::thread::sleep(std::time::Duration::from_millis(50));
}
}

#[derive(Clone, Debug)]
pub struct Message<T> {
inner: T,
}

#[actor(Message<String>)]
#[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<Self::Msg>, msg: Self::Msg, sender: Sender) {
self.receive(ctx, msg, sender);
ctx.stop(&ctx.myself);
}
}

impl Receive<Message<String>> for GenericMsgActor {
type Msg = GenericMsgActorMsg;

fn receive(
&mut self,
_ctx: &Context<Self::Msg>,
msg: Message<String>,
_sender: Option<BasicActorRef>,
) {
println!("{}", msg.inner);
}
}

#[test]
fn run_generic_message_actor() {
let sys = ActorSystem::new().unwrap();

let act = sys.actor_of::<GenericMsgActor>("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<T> {
pub inner: T,
}

#[derive(Clone, Debug)]
pub struct Message;
}

#[actor(test_mod::GenericMessage<String>, 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<Self::Msg>, msg: Self::Msg, sender: Sender) {
self.receive(ctx, msg, sender);
ctx.stop(&ctx.myself);
}
}

impl Receive<test_mod::GenericMessage<String>> for PathMsgActor {
type Msg = PathMsgActorMsg;

fn receive(
&mut self,
_ctx: &Context<Self::Msg>,
msg: test_mod::GenericMessage<String>,
_sender: Option<BasicActorRef>,
) {
println!("{}", msg.inner);
}
}

impl Receive<test_mod::Message> for PathMsgActor {
type Msg = PathMsgActorMsg;

fn receive(
&mut self,
_ctx: &Context<Self::Msg>,
_msg: test_mod::Message,
_sender: Option<BasicActorRef>,
) {
println!("message");
}
}

#[test]
fn run_path_message_actor() {
let sys = ActorSystem::new().unwrap();

let act = sys.actor_of::<PathMsgActor>("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));
}
}

0 comments on commit ccd4e5f

Please sign in to comment.