Files
payphone/src/framework/commands/mod.rs
T

114 lines
2.9 KiB
Rust

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<T, E>: 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<Box<dyn Permission<T, E> + Send + Sync + 'static>> {
vec![]
}
fn show_in_help(&self, _rt: &Runtime<T, E>, _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<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
.trim_ascii_start();
match message.split_once(" ") {
Some((name, args)) => Some(CommandParts::new(name, args)),
None => Some(CommandParts::new(message, "")),
}
} else {
None
}
}