ruma-bot

git clone git://archive.git.mtrnord.blog/MTRNord/ruma-bot.git
Log | Files | Refs

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 }