rust_plugin_macro/
lib.rs

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}