diff --git a/euphoxide-bot/examples/examplebot.rs b/euphoxide-bot/examples/examplebot.rs index 3b5d52c..482dee8 100644 --- a/euphoxide-bot/examples/examplebot.rs +++ b/euphoxide-bot/examples/examplebot.rs @@ -1,41 +1,27 @@ use std::time::Duration; -use async_trait::async_trait; use euphoxide::api::Message; use euphoxide_bot::{ + basic::FromHandler, botrulez::{FullHelp, Ping, ShortHelp}, - Command, CommandExt, Commands, Context, Info, Propagate, + CommandExt, Commands, Context, Propagate, }; use euphoxide_client::MultiClient; use log::error; use tokio::sync::mpsc; -struct Pyramid; - -#[async_trait] -impl Command for Pyramid { - fn info(&self, _ctx: &Context) -> Info { - Info::new().with_description("build a pyramid") - } - - async fn execute( - &self, - _arg: &str, - msg: &Message, - ctx: &Context, - ) -> euphoxide::Result { - let mut parent = msg.id; - - for _ in 0..3 { - let first = ctx.reply(parent, "brick").await?; - ctx.reply_only(parent, "brick").await?; - parent = first.await?.0.id; - tokio::time::sleep(Duration::from_secs(1)).await; - } +async fn pyramid(_arg: &str, msg: &Message, ctx: &Context) -> euphoxide::Result { + let mut parent = msg.id; + for _ in 0..3 { + let first = ctx.reply(parent, "brick").await?; ctx.reply_only(parent, "brick").await?; - Ok(Propagate::No) + parent = first.await?.0.id; + tokio::time::sleep(Duration::from_secs(1)).await; } + + ctx.reply_only(parent, "brick").await?; + Ok(Propagate::No) } #[tokio::main] @@ -56,7 +42,12 @@ async fn main() { .specific("help") .hidden(), ) - .then(Pyramid.general("pyramid")) + .then( + FromHandler::new(pyramid) + .described() + .with_description("build a pyramid") + .general("pyramid"), + ) .build(); let clients = MultiClient::new(event_tx); diff --git a/euphoxide-bot/src/command/basic.rs b/euphoxide-bot/src/command/basic.rs index 5f7cac0..9d91cf1 100644 --- a/euphoxide-bot/src/command/basic.rs +++ b/euphoxide-bot/src/command/basic.rs @@ -1,5 +1,7 @@ //! Basic command wrappers. +use std::future::Future; + use async_trait::async_trait; use euphoxide::api::Message; @@ -97,3 +99,43 @@ where } } } + +// Black type magic, thanks a lot to https://github.com/kpreid and the +// async_fn_traits crate! + +// TODO Simplify all this once AsyncFn becomes stable + +pub trait HandlerFn<'a0, 'a1, 'a2, E>: + Fn(&'a0 str, &'a1 Message, &'a2 Context) -> Self::Future +where + E: 'a2, +{ + type Future: Future> + Send; +} + +impl<'a0, 'a1, 'a2, E, F, Fut> HandlerFn<'a0, 'a1, 'a2, E> for F +where + E: 'a2, + F: Fn(&'a0 str, &'a1 Message, &'a2 Context) -> Fut + ?Sized, + Fut: Future> + Send, +{ + type Future = Fut; +} + +pub struct FromHandler(F); + +impl FromHandler { + pub fn new(f: F) -> Self { + Self(f) + } +} + +#[async_trait] +impl Command for FromHandler +where + F: for<'a0, 'a1, 'a2> HandlerFn<'a0, 'a1, 'a2, E> + Sync, +{ + async fn execute(&self, arg: &str, msg: &Message, ctx: &Context) -> Result { + (self.0)(arg, msg, ctx).await + } +}