use std::fmt::Debug; use async_trait::async_trait; use thiserror::Error; use crate::framework::commands::{arguments::Argument, context::CommandContext}; pub mod arguments; pub mod context; pub mod messages; pub mod runner; #[derive(Debug, Error)] pub enum CommandError { #[error("A message was attempted to be sent but it failed")] MessageSendingFailed, #[error("A database query failed")] DatabaseQueryFailed, #[error("A database operation failed due to an inconsistent state")] DatabaseInconsistentState, } #[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] { &[] } /// 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>, 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; match message.split_once(" ") { Some((name, args)) => Some(CommandParts::new(name, args)), None => Some(CommandParts::new(message, "")), } } else { None } }