dragonfly_plugin_macro/
lib.rs1mod command;
16mod plugin;
17
18use heck::ToPascalCase;
19use proc_macro::TokenStream;
20use quote::{format_ident, quote};
21use syn::{parse_macro_input, Attribute, DeriveInput, ImplItem, ItemImpl};
22
23use crate::{command::generate_command_impls, plugin::generate_plugin_impl};
24
25#[proc_macro_derive(Plugin, attributes(plugin, events))]
26pub fn handler_derive(input: TokenStream) -> TokenStream {
27 let ast = parse_macro_input!(input as DeriveInput);
28
29 let derive_name = &ast.ident;
30
31 let info_attr = match find_attribute(
32 &ast,
33 "plugin",
34 "Missing `#[plugin(...)]` attribute with metadata.",
35 ) {
36 Ok(attr) => attr,
37 Err(e) => return e.to_compile_error().into(),
38 };
39
40 let plugin_impl = generate_plugin_impl(info_attr, derive_name);
41
42 quote! {
43 #plugin_impl
44 }
45 .into()
46}
47
48fn find_attribute<'a>(
49 ast: &'a syn::DeriveInput,
50 name: &str,
51 error: &str,
52) -> Result<&'a Attribute, syn::Error> {
53 ast.attrs
54 .iter()
55 .find(|a| a.path().is_ident(name))
56 .ok_or_else(|| syn::Error::new(ast.ident.span(), error))
57}
58
59#[proc_macro_attribute]
60pub fn event_handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
61 let item_clone = item.clone();
62
63 let impl_block = match syn::parse::<ItemImpl>(item) {
65 Ok(block) => block,
66 Err(_) => {
67 return item_clone;
71 }
72 };
73
74 let is_event_handler_impl = if let Some((_, trait_path, _)) = &impl_block.trait_ {
76 trait_path
77 .segments
78 .last()
79 .is_some_and(|segment| segment.ident == "EventHandler")
80 } else {
81 return item_clone;
82 };
83
84 if !is_event_handler_impl {
85 return item_clone;
88 }
89
90 let mut event_variants = Vec::new();
91 for item in &impl_block.items {
92 if let ImplItem::Fn(method) = item {
93 let fn_name = method.sig.ident.to_string();
94
95 if let Some(event_name_snake) = fn_name.strip_prefix("on_") {
96 let event_name_pascal = event_name_snake.to_pascal_case();
97
98 let variant_ident = format_ident!("{}", event_name_pascal);
99 event_variants.push(quote! { types::EventType::#variant_ident });
100 }
101 }
102 }
103
104 let self_ty = &impl_block.self_ty;
105
106 let subscriptions_impl = quote! {
107 impl dragonfly_plugin::EventSubscriptions for #self_ty {
108 fn get_subscriptions(&self) -> Vec<types::EventType> {
109 vec![
110 #( #event_variants ),*
111 ]
112 }
113 }
114 };
115
116 let original_impl_tokens = quote! { #impl_block };
117
118 let final_output = quote! {
119 #original_impl_tokens
120 #subscriptions_impl
121 };
122
123 final_output.into()
124}
125
126#[proc_macro_derive(Command, attributes(command, subcommand))]
127pub fn command_derive(input: TokenStream) -> TokenStream {
128 let ast = parse_macro_input!(input as DeriveInput);
129
130 let command_attr = match find_attribute(
131 &ast,
132 "command",
133 "Missing `#[command(...)]` attribute with metadata.",
134 ) {
135 Ok(attr) => attr,
136 Err(e) => return e.to_compile_error().into(),
137 };
138
139 generate_command_impls(&ast, command_attr).into()
140}