1use proc_macro::TokenStream;
2
3use syn::*;
4use syn::punctuated::Punctuated;
5
6use quote::*;
7
8fn parse_handles(attrs: Vec<Attribute>) -> (Vec<Ident>, Vec<Ident>) {
9 let mut message_idents = Vec::new();
10 let mut reply_idents = Vec::new();
11 for attr in attrs {
12 if attr.path.is_ident("handles") {
13 if let Ok(idents) = attr.parse_args_with(Punctuated::<Ident, Token![,]>::parse_terminated) {
14 if idents.len() == 2 {
15 let mut idents = idents.into_iter();
16 message_idents.push(idents.next().unwrap());
17 reply_idents.push(idents.next().unwrap());
18 }
19 }
20 }
21
22 }
23 (message_idents, reply_idents)
24}
25
26#[proc_macro_derive(Actor, attributes(handles))]
27pub fn derive_message_group(input: TokenStream) -> TokenStream {
28 let input = parse_macro_input!(input as DeriveInput);
29 let input_ident = input.ident;
30
31 let messages_ident = format_ident!("{}Messages", input_ident);
32 let replies_ident = format_ident!("{}Replies", input_ident);
33
34 let (messages, replies) = parse_handles(input.attrs);
35
36 let message_impl = quote! {
37 #(impl ::chikatetsu::message::Message for #messages {
38 type Group = #messages_ident;
39 type Reply = #replies;
40
41 fn to_group(self) -> #messages_ident {
42 #messages_ident::#messages(self)
43 }
44 })*
45 };
46
47 let message_group_enum = quote! {
48 pub enum #messages_ident {
49 #(#messages(#messages)),*
50 }
51
52 impl ::chikatetsu::message::MessageGroup for #messages_ident {
53 type ReplyGroup = #replies_ident;
54 }
55 };
56
57 let reply_group_enum = quote! {
58 pub enum #replies_ident {
59 #(#replies(#replies)),*
60 }
61
62 #(
63 impl ::chikatetsu::message::Reply for #replies {
64 type Group = #replies_ident;
65
66 fn from_group(group: #replies_ident) -> Option<Self> {
67 match group {
68 #replies_ident::#replies(inner) => Some(inner),
69 _ => None,
70 }
71 }
72 }
73 )*
74 };
75
76 let actor_impl = quote! {
77 #[::chikatetsu::async_trait::async_trait]
78 impl ::chikatetsu::actor::Actor for #input_ident {
79 type MessageGroup = #messages_ident;
80 async fn handle_all(&mut self, msg: #messages_ident) -> #replies_ident {
81 match msg {
82 #(#messages_ident::#messages(inner) => #replies_ident::#replies(self.handle(inner).await)),*
83 }
84 }
85 }
86 };
87
88 let output = quote!{
89 #message_impl
90 #message_group_enum
91 #reply_group_enum
92 #actor_impl
93 };
94 TokenStream::from(output)
95}