Skip to content

Commit ccd4e5f

Browse files
authored
Feat/messages with generics (#155)
* Allow generics in actor macro attributes * test generic actor message * actor macro: handle fully qualified message paths
1 parent 641f627 commit ccd4e5f

2 files changed

Lines changed: 152 additions & 13 deletions

File tree

riker-macros/src/lib.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ use proc_macro2::{Ident, TokenStream};
44
use quote::quote;
55
use syn::parse::{Parse, ParseStream, Result};
66
use syn::punctuated::Punctuated;
7-
use syn::{DeriveInput, Generics};
7+
use syn::spanned::Spanned;
8+
use syn::token::{Colon2, Comma};
9+
use syn::{DeriveInput, Generics, PathSegment, TypePath};
810

911
struct MsgTypes {
1012
types: Vec<MsgVariant>,
1113
}
1214

1315
struct MsgVariant {
1416
name: Ident,
15-
mtype: Ident,
17+
mtype: TypePath,
1618
}
1719

1820
impl MsgTypes {
@@ -35,28 +37,38 @@ impl MsgTypes {
3537

3638
impl Parse for MsgTypes {
3739
fn parse(input: ParseStream) -> Result<Self> {
38-
let vars = Punctuated::<Ident, syn::token::Comma>::parse_terminated(input)?;
40+
let vars = Punctuated::<TypePath, Comma>::parse_terminated(input)?;
3941

4042
Ok(MsgTypes {
4143
types: vars
4244
.into_iter()
4345
.map(|t| MsgVariant {
44-
name: get_name(&t),
46+
name: get_name(&t.path.segments),
4547
mtype: t,
4648
})
4749
.collect::<Vec<_>>(),
4850
})
4951
}
5052
}
5153

52-
fn get_name(ident: &Ident) -> Ident {
53-
let mut vname = format!("{}", ident.clone());
54-
55-
if let Some(c) = vname.get_mut(0..1) {
56-
c.make_ascii_uppercase();
57-
}
58-
59-
syn::Ident::new(&vname, ident.span())
54+
fn get_name(segments: &Punctuated<PathSegment, Colon2>) -> Ident {
55+
let vname = segments
56+
.iter()
57+
.map(|seg| {
58+
let ident = format!("{}", seg.ident);
59+
ident
60+
.split('_')
61+
.map(|s| {
62+
let mut s = s.to_string();
63+
if let Some(c) = s.get_mut(0..1) {
64+
c.make_ascii_uppercase();
65+
}
66+
s
67+
})
68+
.collect::<String>()
69+
})
70+
.collect::<String>();
71+
syn::Ident::new(&vname, segments.span())
6072
}
6173

6274
#[proc_macro_attribute]
@@ -123,7 +135,7 @@ fn receive(aname: &Ident, gen: &Generics, name: &Ident, types: &MsgTypes) -> Tok
123135
}
124136
}
125137

126-
fn impl_into(name: &Ident, vname: &Ident, ty: &Ident) -> TokenStream {
138+
fn impl_into(name: &Ident, vname: &Ident, ty: &TypePath) -> TokenStream {
127139
quote! {
128140
impl Into<#name> for #ty {
129141
fn into(self) -> #name {

riker-macros/tests/macro.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,130 @@ fn run_derived_generic_actor() {
9595
std::thread::sleep(std::time::Duration::from_millis(50));
9696
}
9797
}
98+
99+
#[derive(Clone, Debug)]
100+
pub struct Message<T> {
101+
inner: T,
102+
}
103+
104+
#[actor(Message<String>)]
105+
#[derive(Clone, Default)]
106+
struct GenericMsgActor;
107+
108+
impl Actor for GenericMsgActor {
109+
type Msg = GenericMsgActorMsg;
110+
111+
fn supervisor_strategy(&self) -> Strategy {
112+
Strategy::Stop
113+
}
114+
115+
fn recv(&mut self, ctx: &Context<Self::Msg>, msg: Self::Msg, sender: Sender) {
116+
self.receive(ctx, msg, sender);
117+
ctx.stop(&ctx.myself);
118+
}
119+
}
120+
121+
impl Receive<Message<String>> for GenericMsgActor {
122+
type Msg = GenericMsgActorMsg;
123+
124+
fn receive(
125+
&mut self,
126+
_ctx: &Context<Self::Msg>,
127+
msg: Message<String>,
128+
_sender: Option<BasicActorRef>,
129+
) {
130+
println!("{}", msg.inner);
131+
}
132+
}
133+
134+
#[test]
135+
fn run_generic_message_actor() {
136+
let sys = ActorSystem::new().unwrap();
137+
138+
let act = sys.actor_of::<GenericMsgActor>("act").unwrap();
139+
140+
let msg = GenericMsgActorMsg::Message(Message {
141+
inner: "test".to_string(),
142+
});
143+
act.tell(msg, None);
144+
145+
// wait until all direct children of the user root are terminated
146+
while sys.user_root().has_children() {
147+
// in order to lower cpu usage, sleep here
148+
std::thread::sleep(std::time::Duration::from_millis(50));
149+
}
150+
}
151+
152+
mod test_mod {
153+
#[derive(Clone, Debug)]
154+
pub struct GenericMessage<T> {
155+
pub inner: T,
156+
}
157+
158+
#[derive(Clone, Debug)]
159+
pub struct Message;
160+
}
161+
162+
#[actor(test_mod::GenericMessage<String>, test_mod::Message)]
163+
#[derive(Clone, Default)]
164+
struct PathMsgActor;
165+
166+
impl Actor for PathMsgActor {
167+
type Msg = PathMsgActorMsg;
168+
169+
fn supervisor_strategy(&self) -> Strategy {
170+
Strategy::Stop
171+
}
172+
173+
fn recv(&mut self, ctx: &Context<Self::Msg>, msg: Self::Msg, sender: Sender) {
174+
self.receive(ctx, msg, sender);
175+
ctx.stop(&ctx.myself);
176+
}
177+
}
178+
179+
impl Receive<test_mod::GenericMessage<String>> for PathMsgActor {
180+
type Msg = PathMsgActorMsg;
181+
182+
fn receive(
183+
&mut self,
184+
_ctx: &Context<Self::Msg>,
185+
msg: test_mod::GenericMessage<String>,
186+
_sender: Option<BasicActorRef>,
187+
) {
188+
println!("{}", msg.inner);
189+
}
190+
}
191+
192+
impl Receive<test_mod::Message> for PathMsgActor {
193+
type Msg = PathMsgActorMsg;
194+
195+
fn receive(
196+
&mut self,
197+
_ctx: &Context<Self::Msg>,
198+
_msg: test_mod::Message,
199+
_sender: Option<BasicActorRef>,
200+
) {
201+
println!("message");
202+
}
203+
}
204+
205+
#[test]
206+
fn run_path_message_actor() {
207+
let sys = ActorSystem::new().unwrap();
208+
209+
let act = sys.actor_of::<PathMsgActor>("act").unwrap();
210+
211+
let msg = PathMsgActorMsg::TestModMessage(test_mod::Message);
212+
act.tell(msg, None);
213+
214+
let generic_msg = PathMsgActorMsg::TestModGenericMessage(test_mod::GenericMessage {
215+
inner: "test".to_string(),
216+
});
217+
act.tell(generic_msg, None);
218+
219+
// wait until all direct children of the user root are terminated
220+
while sys.user_root().has_children() {
221+
// in order to lower cpu usage, sleep here
222+
std::thread::sleep(std::time::Duration::from_millis(50));
223+
}
224+
}

0 commit comments

Comments
 (0)