actors_macros/
lib.rs

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        // caused "missing extern crate token" compile error
39        // let vars = Punctuated::<Ident, Token![,]>::parse_terminated(input)?;
40        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}