Files
payphone/src/framework/commands/mod.rs
T
2025-08-10 21:15:53 -03:00

79 lines
2.0 KiB
Rust

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<T>: 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<CommandParts<'a>> {
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
}
}