1extern crate proc_macro;
2
3use proc_macro2::{Ident, TokenStream};
4use quote::quote;
5use syn::parse::{Parse, ParseStream, Result};
6use syn::punctuated::Punctuated;
7use syn::DeriveInput;
8
9struct MsgTypes {
10 types: Vec<MsgVariant>,
11}
12
13struct MsgVariant {
14 name: Ident,
15 mtype: Ident,
16}
17
18impl MsgTypes {
19 fn enum_stream(&self, name: &Ident) -> TokenStream {
20 let vars = self.types.iter().map(|t| {
21 let MsgVariant { name, mtype } = t;
22 quote! {
23 #name(#mtype),
24 }
25 });
26
27 quote! {
28 #[derive(Clone, Debug)]
29 pub enum #name {
30 #(#vars)*
31 }
32 }
33 }
34}
35
36impl Parse for MsgTypes {
37 fn parse(input: ParseStream) -> Result<Self> {
38 let vars = Punctuated::<Ident, syn::token::Comma>::parse_terminated(input)?;
41
42 Ok(MsgTypes {
43 types: vars
44 .into_iter()
45 .map(|t| MsgVariant {
46 name: get_name(&t),
47 mtype: t,
48 })
49 .collect::<Vec<_>>(),
50 })
51 }
52}
53
54fn get_name(ident: &Ident) -> Ident {
55 let mut vname = format!("{}", ident.clone());
56
57 if let Some(c) = vname.get_mut(0..1) {
58 c.make_ascii_uppercase();
59 }
60
61 syn::Ident::new(&vname, ident.span())
62}
63
64#[proc_macro_attribute]
65pub fn actor(
66 attr: proc_macro::TokenStream,
67 input: proc_macro::TokenStream,
68) -> proc_macro::TokenStream {
69 let i = input.clone();
70 let ast = syn::parse_macro_input!(i as DeriveInput);
71
72 let name = format!("{}Msg", ast.ident);
73 let name = syn::Ident::new(&name, ast.ident.span());
74 let types = syn::parse_macro_input!(attr as MsgTypes);
75
76 let menum = types.enum_stream(&name);
77 let intos = intos(&name, &types);
78 let rec = receive(&ast.ident, &name, &types);
79
80 let input: TokenStream = input.into();
81 let gen = quote! {
82 #input
83 #menum
84 #intos
85
86 #rec
87 };
88
89 gen.into()
90}
91
92fn intos(name: &Ident, types: &MsgTypes) -> TokenStream {
93 let intos = types
94 .types
95 .iter()
96 .map(|t| impl_into(&name, &t.name, &t.mtype));
97 quote! {
98 #(#intos)*
99 }
100}
101
102fn receive(aname: &Ident, name: &Ident, types: &MsgTypes) -> TokenStream {
103 let vars = types.types.iter().map(|t| {
104 let vname = &t.name;
105 quote! {
106 #name::#vname(msg) => <#aname>::receive(self, ctx, msg, sender),
107 }
108 });
109 quote! {
110 impl Receive<#name> for #aname {
111 type Msg = #name;
112
113 fn receive(&mut self,
114 ctx: &Context<Self::Msg>,
115 msg: #name,
116 sender: Option<BasicActorRef>) {
117 match msg {
118 #(#vars)*
119 }
120 }
121 }
122 }
123}
124
125fn impl_into(name: &Ident, vname: &Ident, ty: &Ident) -> TokenStream {
126 quote! {
127 impl Into<#name> for #ty {
128 fn into(self) -> #name {
129 #name::#vname(self)
130 }
131 }
132 }
133}