use std::fmt::Debug; use async_trait::async_trait; use deadpool_redis::{cluster::PoolError, redis::RedisError}; use thiserror::Error; use twilight_http::response::DeserializeBodyError; use crate::{database::models::{DatabaseError, DatabaseOrDiscordError}, framework::{commands::{ arguments::Argument, context::CommandContext, permissions::Permission, }, runtime::Runtime}}; pub mod arguments; pub mod context; pub mod extras; pub mod messages; pub mod permissions; pub mod runner; #[derive(Debug, Error)] pub enum CommandError { #[error("A database query failed")] DatabaseFailed(#[from] DatabaseError), #[error("A database operation failed due to an inconsistent state")] DatabaseInconsistentState, #[error("The command was used in DMs, but it can only be used in servers")] ServerCommandRunInDMs, #[error(transparent)] DiscordAPIFailed(#[from] twilight_http::Error), #[error(transparent)] DiscordDeserializationFailed(#[from] DeserializeBodyError), #[error(transparent)] RedisPoolFailed(#[from] PoolError), #[error(transparent)] RedisFailed(#[from] RedisError), #[error(transparent)] DatabaseOrDiscordError(#[from] DatabaseOrDiscordError) } #[async_trait] pub trait Command: Send + Sync + Debug { fn name(&self) -> &'static str; fn aliases(&self) -> &'static [&'static str] { &[] } fn description(&self) -> &'static str { "A Payphone command." } fn arguments(&self) -> &'static [Argument] { &[] } fn permissions(&self) -> Vec + Send + Sync + 'static>> { vec![] } fn show_in_help(&self, _rt: &Runtime, _extras: &E) -> bool { true } /// Returns the command name + all its aliases. /// /// Returns: /// [`Vec<&'static str>`] - A vector with the command's name and its aliases. fn identifiers(&self) -> Vec<&'static str> { let mut all = vec![self.name()]; all.extend_from_slice(self.aliases()); all } async fn execute(&self, ctx: CommandContext<'_, T, E>, args: &str) -> Result<(), CommandError>; } pub struct CommandParts<'a> { pub name: &'a str, pub args: &'a str, } impl<'a> CommandParts<'a> { fn new(name: &'a str, args: &'a str) -> Self { Self { name, args } } } pub fn get_command_parts<'a>(prefix: &str, message: &'a str) -> Option> { let message_trimmed_start = message.trim_ascii_start(); if message_trimmed_start .to_ascii_lowercase() .starts_with(&prefix.to_ascii_lowercase()) { let message = message_trimmed_start .split_at(prefix.len()) .1 .trim_ascii_start(); match message.split_once(" ") { Some((name, args)) => Some(CommandParts::new(name, args)), None => Some(CommandParts::new(message, "")), } } else { None } }