Skip to main content

chikatetsu_macros/
lib.rs

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}