ruma-bot

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

commit 84dd9264eb21e4584f53663b969e03329d3a861e
parent daa9349cfe2c7b22b762762c89cbe0fc2a97a2fe
Author: Jonas Platte <jplatte+git@posteo.de>
Date:   Sun, 28 Jul 2019 19:09:31 +0200

Improve command handler call logic

Diffstat:
Mmacro/src/args.rs | 2+-
Mmacro/src/lib.rs | 62+++++++++++++++++++++++++++++---------------------------------
Dsrc/command_handler_fn.rs | 44--------------------------------------------
Msrc/lib.rs | 14++++++++------
Msrc/state.rs | 3++-
Asrc/util.rs | 24++++++++++++++++++++++++
Mtests/echo.rs | 10+++++++++-
7 files changed, 73 insertions(+), 86 deletions(-)

diff --git a/macro/src/args.rs b/macro/src/args.rs @@ -71,7 +71,7 @@ impl Parse for Arg { } } -pub struct Args(Vec<Arg>); +pub struct Args(pub Vec<Arg>); impl Parse for Args { fn parse(input: ParseStream) -> syn::Result<Self> { diff --git a/macro/src/lib.rs b/macro/src/lib.rs @@ -5,9 +5,9 @@ use std::mem; use proc_macro2::Span; use quote::quote; -use syn::{parse_macro_input, Ident, ItemFn}; +use syn::{parse_macro_input, Ident, ItemFn, LitStr}; -use args::Args; +use args::{Arg, Args}; mod args; @@ -18,57 +18,53 @@ pub fn command_handler(args: TokenStream, input: TokenStream) -> TokenStream { assert!( handler_fn.decl.variadic.is_none(), - "ruma_bot handler functions are not allowed to be variadic", + "handler functions are not allowed to be variadic", ); assert!( handler_fn.unsafety.is_none(), - "ruma_bot handler functions are not allowed to be `unsafe`", + "handler functions are not allowed to be `unsafe`", ); - let infer_type = syn::Type::Infer(syn::TypeInfer { - underscore_token: Default::default(), - }); - let fn_type = syn::TypeBareFn { - lifetimes: None, - unsafety: None, - abi: handler_fn.abi.clone(), - fn_token: Default::default(), - paren_token: Default::default(), - inputs: (0..handler_fn.decl.inputs.len()) - .map(|_| syn::BareFnArg { - name: None, - ty: infer_type.clone(), - }) - .collect(), - variadic: None, - output: syn::ReturnType::Type(Default::default(), Box::new(infer_type)), - }; - - let ident = mem::replace( - &mut handler_fn.ident, - Ident::new("command_handler_impl", Span::call_site()), - ); - // TODO - let commands = vec![ident.to_string()]; + let fn_ident = Ident::new(&format!("_{}_impl", handler_fn.ident), Span::call_site()); + let ident = mem::replace(&mut handler_fn.ident, fn_ident.clone()); + + let get_calls = (0..handler_fn.decl.inputs.len()).map(|_| quote!(param_matcher.get())); + + let mut commands = Vec::new(); + for arg in macro_args.0 { + match arg { + Arg::Command(cmd_arg) => commands.push(cmd_arg), + Arg::Commands(cmd_args) => commands.extend(cmd_args), + } + } + + // If no commands are given as arguments to this proc macro, use the function name as the + // command name + if commands.is_empty() { + commands.push(LitStr::new(&ident.to_string(), Span::call_site())); + } TokenStream::from(quote! { #[allow(non_camel_case_types)] #[derive(Clone, Copy)] struct #ident; + #handler_fn + impl ruma_bot::CommandHandler for #ident { fn commands() -> &'static [&'static str] { &[#(#commands),*] } - fn call( - &mut self, + fn handle( + &self, bot: &ruma_bot::Bot, ) -> Box<dyn futures::Future<Output = Result<(), failure::Error>>> { - #handler_fn + use ruma_bot::GetParam; + let param_matcher = ruma_bot::HandlerParamMatcher { bot }; - ruma_bot::CommandHandlerFn::call(command_handler_impl as #fn_type, bot) + Box::new(#fn_ident(#(#get_calls),*)) } } }) diff --git a/src/command_handler_fn.rs b/src/command_handler_fn.rs @@ -1,44 +0,0 @@ -use failure::Error; -use futures::{Future, FutureExt}; - -use crate::Bot; - -pub trait CommandHandlerFn { - fn call(self, bot: &Bot) -> Box<dyn Future<Output = Result<(), Error>> + Send>; -} - -/*impl<R> CommandHandlerFn for fn() -> R -where - R: Future<Output = ()> + Send + 'static, // TODO: 'static bound reasonable? -{ - fn call(self, _bot: &Bot) -> Box<dyn Future<Output = Result<(), Error>> + Send> { - Box::new((self)().map(|_| Ok(()))) - } -} - -impl<R> CommandHandlerFn for fn(&Bot) -> R -where - R: Future<Output = ()> + Send + 'static, // TODO: 'static bound reasonable? -{ - fn call(self, bot: &Bot) -> Box<dyn Future<Output = Result<(), Error>> + Send> { - Box::new((self)(bot).map(|_| Ok(()))) - } -}*/ - -impl<R> CommandHandlerFn for fn() -> R -where - R: Future<Output = Result<(), Error>> + Send + 'static, // TODO: 'static bound reasonable? -{ - fn call(self, _bot: &Bot) -> Box<dyn Future<Output = Result<(), Error>> + Send> { - Box::new((self)()) - } -} - -impl<R> CommandHandlerFn for fn(&Bot) -> R -where - R: Future<Output = Result<(), Error>> + Send + 'static, // TODO: 'static bound reasonable? -{ - fn call(self, bot: &Bot) -> Box<dyn Future<Output = Result<(), Error>> + Send> { - Box::new((self)(bot)) - } -} diff --git a/src/lib.rs b/src/lib.rs @@ -10,23 +10,25 @@ use std::{collections::HashMap, sync::Arc}; -use anymap::any::Any; +use anymap::any::CloneAny; use failure::{err_msg, Fallible}; use futures::{Future, TryStreamExt}; use js_int::UInt; use ruma_client::{api, HttpsClient as MatrixClient}; use url::Url; -type AnyMap = anymap::Map<dyn Any + Send + Sync>; +// TODO: Get rid of compiler warnings, either by replacing CloneAny, or by fixing the issue upstream +type AnyMap = anymap::Map<dyn CloneAny + Send + Sync>; type HandlerFnMap = HashMap<&'static str, Box<dyn CommandHandler>>; pub use ruma_bot_macro::command_handler; -mod command_handler_fn; mod state; +mod util; +pub use state::State; #[doc(hidden)] -pub use command_handler_fn::CommandHandlerFn; +pub use util::{GetParam, HandlerParamMatcher}; fn env_var(name: &'static str) -> Option<String> { std::env::var(name) @@ -46,8 +48,8 @@ pub trait CommandHandler: Send + Sync { Self: Sized; /// Used to call the command handler with the necessary application state and command context - // TODO: Additional parameter(s) for command details - fn call(&mut self, bot: &Bot) -> Box<dyn Future<Output = Result<(), failure::Error>>>; + // TODO: Additional parameter(s) for command data + fn handle(&self, _: &Bot) -> Box<dyn Future<Output = Result<(), failure::Error>>>; } struct ConnectionDetails { diff --git a/src/state.rs b/src/state.rs @@ -3,7 +3,8 @@ use std::{ ops::Deref, }; -struct State<T: Send + Sync> { +#[derive(Clone, Copy)] +pub struct State<T: Send + Sync> { inner: T, } diff --git a/src/util.rs b/src/util.rs @@ -0,0 +1,24 @@ +use crate::{Bot, State}; + +// Currently, GetParam could also be implemented direclty on &'a Bot, but in the future, +// event-specific data will be added to this struct. +pub struct HandlerParamMatcher<'a> { + pub bot: &'a Bot, +} + +pub trait GetParam<T> { + fn get(&self) -> Option<T>; +} + +impl<'a> GetParam<&'a Bot> for HandlerParamMatcher<'a> { + fn get(&self) -> Option<&'a Bot> { + Some(self.bot) + } +} + +// TODO: There should be a way of replacing 'static with 'a here without getting an error +impl<'a, T: Clone + Send + Sync + 'static> GetParam<State<T>> for HandlerParamMatcher<'a> { + fn get(&self) -> Option<State<T>> { + self.bot.state.get().cloned() + } +} diff --git a/tests/echo.rs b/tests/echo.rs @@ -3,11 +3,19 @@ use failure::Fallible; use ruma_bot::{command_handler, BotBuilder}; -#[command_handler(command = "help")] +#[command_handler] async fn help() -> Fallible<()> { Ok(()) } +#[command_handler(commands = ["x", "y", "z"])] +async fn test() -> Fallible<()> { + Ok(()) +} + +//#[command_handler(command = "fetch {id}")] +//#[command_handler(command = regex("a(B|CD) (\w+)"))] + #[tokio::main] async fn main() { let bot = BotBuilder::new().register(help).build();