dragonfly_plugin_macro/
lib.rs1mod plugin;
2
3use heck::ToPascalCase;
4use proc_macro::TokenStream;
5use quote::{format_ident, quote};
6use syn::{parse_macro_input, Attribute, DeriveInput, ImplItem, ItemImpl};
7
8use crate::plugin::generate_plugin_impl;
9
10#[proc_macro_derive(Plugin, attributes(plugin, events))]
11pub fn handler_derive(input: TokenStream) -> TokenStream {
12 let ast = parse_macro_input!(input as DeriveInput);
13
14 let derive_name = &ast.ident;
15
16 let info_attr = match find_attribute(
17 &ast,
18 "plugin",
19 "Missing `#[plugin(...)]` attribute with metadata.",
20 ) {
21 Ok(attr) => attr,
22 Err(e) => return e.to_compile_error().into(),
23 };
24
25 let plugin_impl = generate_plugin_impl(info_attr, derive_name);
26
27 quote! {
28 #plugin_impl
29 }
30 .into()
31}
32
33fn find_attribute<'a>(
34 ast: &'a syn::DeriveInput,
35 name: &str,
36 error: &str,
37) -> Result<&'a Attribute, syn::Error> {
38 ast.attrs
39 .iter()
40 .find(|a| a.path().is_ident(name))
41 .ok_or_else(|| syn::Error::new(ast.ident.span(), error))
42}
43
44#[proc_macro_attribute]
45pub fn event_handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
46 let item_clone = item.clone();
47
48 let impl_block = match syn::parse::<ItemImpl>(item) {
50 Ok(block) => block,
51 Err(_) => {
52 return item_clone;
56 }
57 };
58
59 let is_event_handler_impl = if let Some((_, trait_path, _)) = &impl_block.trait_ {
61 trait_path
62 .segments
63 .last()
64 .is_some_and(|segment| segment.ident == "EventHandler")
65 } else {
66 return item_clone;
67 };
68
69 if !is_event_handler_impl {
70 return item_clone;
73 }
74
75 let mut event_variants = Vec::new();
76 for item in &impl_block.items {
77 if let ImplItem::Fn(method) = item {
78 let fn_name = method.sig.ident.to_string();
79
80 if let Some(event_name_snake) = fn_name.strip_prefix("on_") {
81 let event_name_pascal = event_name_snake.to_pascal_case();
82
83 let variant_ident = format_ident!("{}", event_name_pascal);
84 event_variants.push(quote! { types::EventType::#variant_ident });
85 }
86 }
87 }
88
89 let self_ty = &impl_block.self_ty;
90
91 let subscriptions_impl = quote! {
92 impl dragonfly_plugin::EventSubscriptions for #self_ty {
93 fn get_subscriptions(&self) -> Vec<types::EventType> {
94 vec![
95 #( #event_variants ),*
96 ]
97 }
98 }
99 };
100
101 let original_impl_tokens = quote! { #impl_block };
102
103 let final_output = quote! {
104 #original_impl_tokens
105 #subscriptions_impl
106 };
107
108 final_output.into()
109}