Construct clap commands from handlers too

This commit is contained in:
Joscha 2024-12-29 13:59:27 +01:00
parent a73ababe02
commit a527b22515
3 changed files with 88 additions and 0 deletions

View file

@ -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 {}`

View file

@ -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;