1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4 Data, DeriveInput, Ident, Token,
5 parse::{Parse, ParseStream},
6 parse_macro_input,
7 punctuated::Punctuated,
8};
9
10#[proc_macro_derive(Handler, attributes(subscriptions))]
11pub fn handler_derive(input: TokenStream) -> TokenStream {
12 let ast = parse_macro_input!(input as DeriveInput);
13 if !matches!(&ast.data, Data::Struct(_)) {
14 let msg = "The #[derive(Handler)] macro can only be used on a `struct`.";
15 return syn::Error::new_spanned(&ast.ident, msg)
16 .to_compile_error()
17 .into();
18 };
19
20 let attr = match ast
21 .attrs
22 .iter()
23 .find(|a| a.path().is_ident("subscriptions"))
24 {
25 Some(attr) => attr,
26 None => {
27 let msg = "Missing #[subscriptions(...)] attribute. Please list the events to subscribe to, e.g., #[subscriptions(Chat, PlayerJoin)]";
28 return syn::Error::new_spanned(&ast.ident, msg)
29 .to_compile_error()
30 .into();
31 }
32 };
33
34 let subscriptions = match attr.parse_args::<SubscriptionsListParser>() {
35 Ok(list) => list.events,
36 Err(e) => {
37 return e.to_compile_error().into();
38 }
39 };
40
41 let subscription_variants = subscriptions.iter().map(|ident| {
42 quote! { types::EventType::#ident }
43 });
44
45 let struct_name = &ast.ident;
46
47 let output = quote! {
48 impl dragonfly_plugin::PluginSubscriptions for #struct_name {
49 fn get_subscriptions(&self) -> Vec<types::EventType> {
50 vec![
51 #( #subscription_variants ),*
52 ]
53 }
54 }
55 };
56
57 output.into()
58}
59
60struct SubscriptionsListParser {
61 events: Vec<Ident>,
62}
63
64impl Parse for SubscriptionsListParser {
65 fn parse(input: ParseStream) -> syn::Result<Self> {
66 let punctuated_list: Punctuated<syn::Ident, Token![,]> =
67 Punctuated::parse_terminated(input)?;
68
69 let events = punctuated_list.into_iter().collect();
70
71 Ok(Self { events })
72 }
73}