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:
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();