Construct clap commands from handlers too
This commit is contained in:
parent
30640b393a
commit
0941605ad0
3 changed files with 88 additions and 0 deletions
|
|
@ -4,6 +4,7 @@ use euphoxide::api::Message;
|
|||
use euphoxide_bot::{
|
||||
basic::FromHandler,
|
||||
botrulez::{FullHelp, Ping, ShortHelp},
|
||||
clap::FromClapHandler,
|
||||
CommandExt, Commands, Context, Propagate,
|
||||
};
|
||||
use euphoxide_client::MultiClient;
|
||||
|
|
@ -24,6 +25,21 @@ async fn pyramid(_arg: &str, msg: &Message, ctx: &Context) -> euphoxide::Result<
|
|||
Ok(Propagate::No)
|
||||
}
|
||||
|
||||
#[derive(clap::Parser)]
|
||||
struct AddArgs {
|
||||
lhs: i64,
|
||||
rhs: i64,
|
||||
}
|
||||
|
||||
async fn add(args: AddArgs, msg: &Message, ctx: &Context) -> euphoxide::Result<Propagate> {
|
||||
let result = args.lhs + args.rhs;
|
||||
|
||||
ctx.reply_only(msg.id, format!("{} + {} = {result}", args.lhs, args.rhs))
|
||||
.await?;
|
||||
|
||||
Ok(Propagate::No)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let (event_tx, mut event_rx) = mpsc::channel(10);
|
||||
|
|
@ -48,6 +64,13 @@ async fn main() {
|
|||
.with_description("build a pyramid")
|
||||
.general("pyramid"),
|
||||
)
|
||||
.then(
|
||||
FromClapHandler::new(add)
|
||||
.clap()
|
||||
.with_info()
|
||||
.with_description("add two numbers")
|
||||
.general("add"),
|
||||
)
|
||||
.build();
|
||||
|
||||
let clients = MultiClient::new(event_tx);
|
||||
|
|
|
|||
|
|
@ -148,6 +148,11 @@ pub trait CommandExt: Sized {
|
|||
fn specific(self, name: impl ToString) -> Specific<Self> {
|
||||
Specific::new(name, self)
|
||||
}
|
||||
|
||||
#[cfg(feature = "clap")]
|
||||
fn clap(self) -> clap::Clap<Self> {
|
||||
clap::Clap(self)
|
||||
}
|
||||
}
|
||||
|
||||
// Sadly this doesn't work: `impl<E, C: Command<E>> CommandExt for C {}`
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
//! [`clap`]-based commands.
|
||||
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use clap::{CommandFactory, Parser};
|
||||
use euphoxide::api::Message;
|
||||
|
|
@ -136,6 +138,64 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// TODO Simplify all this once AsyncFn becomes stable
|
||||
|
||||
pub trait ClapHandlerFn<'a0, 'a1, A, E>:
|
||||
Fn(A, &'a0 Message, &'a1 Context<E>) -> Self::Future
|
||||
where
|
||||
E: 'a1,
|
||||
{
|
||||
type Future: Future<Output = Result<Propagate, E>> + Send;
|
||||
}
|
||||
|
||||
impl<'a0, 'a1, A, E, F, Fut> ClapHandlerFn<'a0, 'a1, A, E> for F
|
||||
where
|
||||
E: 'a1,
|
||||
F: Fn(A, &'a0 Message, &'a1 Context<E>) -> Fut + ?Sized,
|
||||
Fut: Future<Output = Result<Propagate, E>> + Send,
|
||||
{
|
||||
type Future = Fut;
|
||||
}
|
||||
|
||||
pub struct FromClapHandler<A, F> {
|
||||
_a: PhantomData<A>,
|
||||
pub handler: F,
|
||||
}
|
||||
|
||||
impl<A, F> FromClapHandler<A, F> {
|
||||
// Artificially constrained so we don't accidentally choose an incorrect A.
|
||||
// Relying on type inference of A can result in unknown type errors even
|
||||
// though we know what A should be based on F.
|
||||
pub fn new<'a0, 'a1, E, Fut>(handler: F) -> Self
|
||||
where
|
||||
F: Fn(A, &'a0 Message, &'a1 Context<E>) -> Fut,
|
||||
E: 'a1,
|
||||
{
|
||||
Self {
|
||||
_a: PhantomData,
|
||||
handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<A, E, F> ClapCommand<E> for FromClapHandler<A, F>
|
||||
where
|
||||
F: for<'a0, 'a1> ClapHandlerFn<'a0, 'a1, A, E> + Sync,
|
||||
A: Send + Sync + 'static,
|
||||
{
|
||||
type Args = A;
|
||||
|
||||
async fn execute(
|
||||
&self,
|
||||
args: Self::Args,
|
||||
msg: &Message,
|
||||
ctx: &Context<E>,
|
||||
) -> Result<Propagate, E> {
|
||||
(self.handler)(args, msg, ctx).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::parse_quoted_args;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue