Add error type parameter to commands

This commit is contained in:
Joscha 2023-01-24 14:44:17 +01:00
parent 4dcc021f73
commit ec9c4c9dd4
3 changed files with 41 additions and 33 deletions

View file

@ -76,10 +76,10 @@ impl Context {
} }
#[async_trait] #[async_trait]
pub trait Command<B> { pub trait Command<B, E> {
fn description(&self) -> Option<String> { fn description(&self) -> Option<String> {
None None
} }
async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B); async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B) -> Result<(), E>;
} }

View file

@ -2,14 +2,21 @@ use async_trait::async_trait;
use clap::{CommandFactory, Parser}; use clap::{CommandFactory, Parser};
use crate::api::Message; use crate::api::Message;
use crate::conn;
use super::{Command, Context}; use super::{Command, Context};
#[async_trait] #[async_trait]
pub trait ClapCommand<B> { pub trait ClapCommand<B, E> {
type Args; type Args;
async fn execute(&self, args: Self::Args, msg: &Message, ctx: &Context, bot: &mut B); async fn execute(
&self,
args: Self::Args,
msg: &Message,
ctx: &Context,
bot: &mut B,
) -> Result<(), E>;
} }
/// Parse bash-like quoted arguments separated by whitespace. /// Parse bash-like quoted arguments separated by whitespace.
@ -92,22 +99,23 @@ fn parse_quoted_args(text: &str) -> Result<Vec<String>, &'static str> {
pub struct Clap<C>(pub C); pub struct Clap<C>(pub C);
#[async_trait] #[async_trait]
impl<B, C> Command<B> for Clap<C> impl<B, E, C> Command<B, E> for Clap<C>
where where
B: Send, B: Send,
C: ClapCommand<B> + Send + Sync, E: From<conn::Error>,
C: ClapCommand<B, E> + Send + Sync,
C::Args: Parser + Send, C::Args: Parser + Send,
{ {
fn description(&self) -> Option<String> { fn description(&self) -> Option<String> {
C::Args::command().get_about().map(|s| format!("{s}")) C::Args::command().get_about().map(|s| format!("{s}"))
} }
async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B) { async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B) -> Result<(), E> {
let mut args = match parse_quoted_args(arg) { let mut args = match parse_quoted_args(arg) {
Ok(args) => args, Ok(args) => args,
Err(err) => { Err(err) => {
let _ = ctx.reply(msg.id, err); ctx.reply(msg.id, err).await?;
return; return Ok(());
} }
}; };
@ -116,8 +124,8 @@ where
let args = match C::Args::try_parse_from(args) { let args = match C::Args::try_parse_from(args) {
Ok(args) => args, Ok(args) => args,
Err(err) => { Err(err) => {
let _ = ctx.reply(msg.id, format!("{}", err.render())); ctx.reply(msg.id, format!("{}", err.render())).await?;
return; return Ok(());
} }
}; };

View file

@ -46,18 +46,18 @@ pub struct CommandInfo {
pub visible: bool, pub visible: bool,
} }
struct CommandWrapper<B> { struct CommandWrapper<B, E> {
command: Box<dyn Command<B>>, command: Box<dyn Command<B, E>>,
visible: bool, visible: bool,
} }
pub struct Commands<B> { pub struct Commands<B, E> {
global: HashMap<String, CommandWrapper<B>>, global: HashMap<String, CommandWrapper<B, E>>,
general: HashMap<String, CommandWrapper<B>>, general: HashMap<String, CommandWrapper<B, E>>,
specific: HashMap<String, CommandWrapper<B>>, specific: HashMap<String, CommandWrapper<B, E>>,
} }
impl<B> Commands<B> { impl<B, E> Commands<B, E> {
/// Global commands always respond. They override any specific or general /// Global commands always respond. They override any specific or general
/// commands of the same name. /// commands of the same name.
/// ///
@ -65,7 +65,7 @@ impl<B> Commands<B> {
pub fn global<S, C>(mut self, name: S, command: C, visible: bool) -> Self pub fn global<S, C>(mut self, name: S, command: C, visible: bool) -> Self
where where
S: ToString, S: ToString,
C: Command<B> + 'static, C: Command<B, E> + 'static,
{ {
let command = Box::new(command); let command = Box::new(command);
let info = CommandWrapper { command, visible }; let info = CommandWrapper { command, visible };
@ -80,7 +80,7 @@ impl<B> Commands<B> {
pub fn general<S, C>(mut self, name: S, command: C, visible: bool) -> Self pub fn general<S, C>(mut self, name: S, command: C, visible: bool) -> Self
where where
S: ToString, S: ToString,
C: Command<B> + 'static, C: Command<B, E> + 'static,
{ {
let command = Box::new(command); let command = Box::new(command);
let info = CommandWrapper { command, visible }; let info = CommandWrapper { command, visible };
@ -92,7 +92,7 @@ impl<B> Commands<B> {
pub fn specific<S, C>(mut self, name: S, command: C, visible: bool) -> Self pub fn specific<S, C>(mut self, name: S, command: C, visible: bool) -> Self
where where
S: ToString, S: ToString,
C: Command<B> + 'static, C: Command<B, E> + 'static,
{ {
let command = Box::new(command); let command = Box::new(command);
let info = CommandWrapper { command, visible }; let info = CommandWrapper { command, visible };
@ -149,20 +149,20 @@ impl<B> Commands<B> {
packet: &ParsedPacket, packet: &ParsedPacket,
snapshot: &Snapshot, snapshot: &Snapshot,
bot: &mut B, bot: &mut B,
) -> bool { ) -> Result<bool, E> {
let msg = match &packet.content { let msg = match &packet.content {
Ok(Data::SendEvent(SendEvent(msg))) => msg, Ok(Data::SendEvent(SendEvent(msg))) => msg,
_ => return false, _ => return Ok(false),
}; };
let joined = match &snapshot.state { let joined = match &snapshot.state {
conn::State::Joining(_) => return false, conn::State::Joining(_) => return Ok(false),
conn::State::Joined(joined) => joined.clone(), conn::State::Joined(joined) => joined.clone(),
}; };
let (cmd_name, rest) = match parse_command(&msg.content) { let (cmd_name, rest) = match parse_command(&msg.content) {
Some(parsed) => parsed, Some(parsed) => parsed,
None => return false, None => return Ok(false),
}; };
let mut ctx = Context { let mut ctx = Context {
@ -175,8 +175,8 @@ impl<B> Commands<B> {
if let Some(wrapper) = self.global.get(cmd_name) { if let Some(wrapper) = self.global.get(cmd_name) {
ctx.kind = Kind::Global; ctx.kind = Kind::Global;
wrapper.command.execute(rest, msg, &ctx, bot).await; wrapper.command.execute(rest, msg, &ctx, bot).await?;
return true; return Ok(true);
} }
if let Some((cmd_nick, rest)) = parse_specific(rest) { if let Some((cmd_nick, rest)) = parse_specific(rest) {
@ -185,8 +185,8 @@ impl<B> Commands<B> {
let cmd_nick_norm = normalize_specific_nick(cmd_nick); let cmd_nick_norm = normalize_specific_nick(cmd_nick);
if nick_norm == cmd_nick_norm { if nick_norm == cmd_nick_norm {
ctx.kind = Kind::Specific; ctx.kind = Kind::Specific;
wrapper.command.execute(rest, msg, &ctx, bot).await; wrapper.command.execute(rest, msg, &ctx, bot).await?;
return true; return Ok(true);
} }
} }
@ -196,16 +196,16 @@ impl<B> Commands<B> {
// //
// To call a specific command with a mention as its first positional // To call a specific command with a mention as its first positional
// argument, -- can be used. // argument, -- can be used.
return false; return Ok(false);
} }
if let Some(wrapper) = self.general.get(cmd_name) { if let Some(wrapper) = self.general.get(cmd_name) {
ctx.kind = Kind::General; ctx.kind = Kind::General;
wrapper.command.execute(rest, msg, &ctx, bot).await; wrapper.command.execute(rest, msg, &ctx, bot).await?;
return true; return Ok(true);
} }
false Ok(false)
} }
} }