teloxide_plugins_macros/
lib.rs1#![allow(non_snake_case)]
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, ItemFn};
6
7#[proc_macro_attribute]
8pub fn TeloxidePlugin(args: TokenStream, input: TokenStream) -> TokenStream {
9 let input_fn = parse_macro_input!(input as ItemFn);
10
11 let args_str = args.to_string();
12
13 let fn_name = &input_fn.sig.ident;
14 let fn_name_str = fn_name.to_string();
15 let ctor_fn_name = syn::Ident::new(&format!("{}_ctor", fn_name_str), fn_name.span());
16 let static_name = syn::Ident::new(&format!("{}_meta", fn_name_str), fn_name.span());
17 let vis = &input_fn.vis;
18 let sig = &input_fn.sig;
19 let block = &input_fn.block;
20
21 let mut commands = Vec::new();
22 let mut prefixes = Vec::new();
23 let mut regex = None;
24 let mut callback_filter = None;
25
26 if args_str.contains("commands = [") {
27 if let Some(start) = args_str.find("commands = [") {
28 if let Some(end) = args_str[start..].find("]") {
29 let commands_str = &args_str[start + 12..start + end];
30 commands = commands_str
31 .split(',')
32 .map(|s| s.trim().trim_matches('"'))
33 .filter(|s| !s.is_empty())
34 .collect();
35 }
36 }
37 }
38
39 if args_str.contains("prefixes = [") {
40 if let Some(start) = args_str.find("prefixes = [") {
41 if let Some(end) = args_str[start..].find("]") {
42 let prefixes_str = &args_str[start + 12..start + end];
43 prefixes = prefixes_str
44 .split(',')
45 .map(|s| s.trim().trim_matches('"'))
46 .filter(|s| !s.is_empty())
47 .collect();
48 }
49 }
50 }
51
52 if args_str.contains("regex = [") {
53 if let Some(start) = args_str.find("regex = [") {
54 if let Some(end) = args_str[start..].find("]") {
55 let regex_str = &args_str[start + 9..start + end];
56 let regex_patterns: Vec<&str> = regex_str
57 .split(',')
58 .map(|s| s.trim().trim_matches('"'))
59 .filter(|s| !s.is_empty())
60 .collect();
61 regex = regex_patterns.first().copied();
62 }
63 }
64 }
65
66 if args_str.contains("callback = [") {
67 if let Some(start) = args_str.find("callback = [") {
68 if let Some(end) = args_str[start..].find("]") {
69 let callback_str = &args_str[start + 12..start + end];
70 let callback_patterns: Vec<&str> = callback_str
71 .split(',')
72 .map(|s| s.trim().trim_matches('"'))
73 .filter(|s| !s.is_empty())
74 .collect();
75 callback_filter = callback_patterns.first().copied();
76 }
77 }
78 }
79
80 let commands_lit: Vec<proc_macro2::TokenStream> =
81 commands.iter().map(|c| quote! { #c }).collect();
82 let prefixes_lit: Vec<proc_macro2::TokenStream> =
83 prefixes.iter().map(|p| quote! { #p }).collect();
84 let regex_lit = match regex {
85 Some(r) => quote! { Some(#r) },
86 None => quote! { None },
87 };
88 let callback_filter_lit = match callback_filter {
89 Some(c) => quote! { Some(#c) },
90 None => quote! { None },
91 };
92
93 let callback_handler = if callback_filter.is_some() {
94 quote! {
95 |ctx| Box::pin(async move {
96 if let Some(cq) = ctx.callback_query.clone() {
97 #fn_name(ctx.bot.clone(), cq).await;
98 }
99 })
100 }
101 } else {
102 quote! {
103 |ctx| Box::pin(async move {
104 if let Some(msg) = ctx.message.clone() {
105 #fn_name(ctx.bot.clone(), msg).await;
106 }
107 })
108 }
109 };
110
111 let expanded = quote! {
112 #vis #sig #block
113
114 #[allow(non_upper_case_globals)]
115 #[doc(hidden)]
116 static #static_name: &teloxide_plugins::registry::PluginMeta = &teloxide_plugins::registry::PluginMeta {
117 name: #fn_name_str,
118 commands: &[#(#commands_lit),*],
119 prefixes: &[#(#prefixes_lit),*],
120 regex: #regex_lit,
121 callback_filter: #callback_filter_lit,
122 callback: #callback_handler,
123 };
124
125 #[ctor::ctor]
126 fn #ctor_fn_name() {
127 if let Ok(handle) = tokio::runtime::Handle::try_current() {
128 handle.spawn(async move {
129 let mut reg = teloxide_plugins::registry::PluginRegistry.lock().await;
130 reg.push(#static_name);
131 });
132 } else {
133 tokio::runtime::Runtime::new()
134 .unwrap()
135 .block_on(async {
136 let mut reg = teloxide_plugins::registry::PluginRegistry.lock().await;
137 reg.push(#static_name);
138 });
139 }
140 }
141 };
142
143 TokenStream::from(expanded)
144}