lib.rs (2397B)
1 extern crate proc_macro; 2 3 use proc_macro::TokenStream; 4 use std::mem; 5 6 use proc_macro2::Span; 7 use quote::quote; 8 use syn::{parse_macro_input, Ident, ItemFn, LitStr}; 9 10 use args::{Arg, Args}; 11 12 mod args; 13 14 #[proc_macro_attribute] 15 pub fn command_handler(args: TokenStream, input: TokenStream) -> TokenStream { 16 let mut handler_fn = parse_macro_input!(input as ItemFn); 17 let macro_args = parse_macro_input!(args as Args); 18 19 assert!( 20 handler_fn.sig.variadic.is_none(), 21 "handler functions are not allowed to be variadic", 22 ); 23 24 assert!( 25 handler_fn.sig.unsafety.is_none(), 26 "handler functions are not allowed to be `unsafe`", 27 ); 28 29 let fn_ident = Ident::new( 30 &format!("_{}_impl", handler_fn.sig.ident), 31 Span::call_site(), 32 ); 33 let ident = mem::replace(&mut handler_fn.sig.ident, fn_ident.clone()); 34 35 // TODO: Error handling (required for State parameters) 36 let get_calls = (0..handler_fn.sig.inputs.len()).map(|_| quote!(param_matcher.get().unwrap())); 37 38 let mut commands = Vec::new(); 39 for arg in macro_args.0 { 40 match arg { 41 Arg::Command(cmd_arg) => commands.push(cmd_arg), 42 Arg::Commands(cmd_args) => commands.extend(cmd_args), 43 } 44 } 45 46 // If no commands are given as arguments to this proc macro, use the function name as the 47 // command name 48 if commands.is_empty() { 49 commands.push(LitStr::new(&ident.to_string(), Span::call_site())); 50 } 51 52 TokenStream::from(quote! { 53 #[allow(non_camel_case_types)] 54 #[derive(Clone, Copy)] 55 struct #ident; 56 57 #handler_fn 58 59 impl ruma_bot::CommandHandler for #ident { 60 fn commands() -> &'static [&'static str] { 61 &[#(#commands),*] 62 } 63 64 fn handle( 65 &self, 66 bot: &ruma_bot::Bot, 67 msg_content: &str, 68 ) -> std::pin::Pin<Box<dyn futures::Future<Output = ()> + Send>> { 69 use ruma_bot::GetParam; 70 let param_matcher = ruma_bot::HandlerParamMatcher { bot, msg_content }; 71 let fut = #fn_ident(#(#get_calls),*); 72 73 Box::pin(async move { 74 let res = fut.await; 75 if let Err(e) = res { 76 eprintln!("{}", e); 77 } 78 }) 79 } 80 } 81 }) 82 }