use std::{error::Error, fmt::Display};
use serde_json::json;
use serenity::futures::future::BoxFuture;
use serde::Serialize;
use crate::framework::CommandContext;
#[derive(Serialize)]
pub struct Command {
pub name: &'static str,
pub description: &'static str,
#[serde(flatten)]
pub arguments_tree: CommandArgumentsTree,
}
pub type CommandFunction = fn(&CommandContext) -> BoxFuture<CommandResult>;
pub type CommandResult<T = ()> = Result<T, CommandError>;
pub type CommandError = Box<dyn Error + Send + Sync>;
#[derive(Debug)]
pub struct SlashyError (String);
impl SlashyError {
pub fn new(err: &str) -> Self {
SlashyError(err.to_string())
}
}
impl Error for SlashyError {}
impl Display for SlashyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Serialize)]
pub struct CommandArgumentsTree {
#[serde(rename = "options")]
pub children: Option<Vec<CommandArguments>>,
#[serde(skip)]
pub func: Option<CommandFunction>,
}
#[allow(missing_docs)]
pub enum CommandArguments {
SubCommand {
name: &'static str,
description: &'static str,
required: bool,
options: Option<Vec<CommandArguments>>,
func: Option<CommandFunction>,
},
SubCommandGroup {
name: &'static str,
description: &'static str,
required: bool,
options: Option<Vec<CommandArguments>>,
func: Option<CommandFunction>,
},
String {
name: &'static str,
description: &'static str,
required: bool,
choices: Option<Vec<ArgumentChoice<String>>>,
},
Integer {
name: &'static str,
description: &'static str,
required: bool,
choices: Option<Vec<ArgumentChoice<i32>>>,
},
Boolean {
name: &'static str,
description: &'static str,
required: bool,
},
User {
name: &'static str,
description: &'static str,
required: bool,
},
Channel {
name: &'static str,
description: &'static str,
required: bool,
},
Role {
name: &'static str,
description: &'static str,
required: bool,
},
}
macro_rules! command_options_serialize {
($self: ident, $map: ident, $($val: path, $type_val: expr, $( $i:ident),* | $($i1:ident),*);*) => {
match $self {
$(
$val { $($i,)* $($i1,)* ..} => {
$map.insert("type".to_owned(), json!($type_val));
$($map.insert(stringify!($i).to_owned(), json!($i)));*;
$(if let Some(t) = $i1 {
$map.insert(stringify!($i1).to_owned(), json!(t));
})*
}),*
#[allow(unreachable_patterns)]
_ => {}
}
};
}
impl Serialize for CommandArguments {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serde_json::Map::new();
command_options_serialize!(
self, map,
CommandArguments::SubCommand, 1, description, name, required | options;
CommandArguments::SubCommandGroup, 2, name, description, required | options;
CommandArguments::String, 3, name, description, required| choices;
CommandArguments::Integer, 4, name, description, required| choices;
CommandArguments::Boolean, 5, name, description, required|;
CommandArguments::User, 6, name, description, required|;
CommandArguments::Channel, 7, name, description, required|;
CommandArguments::Role, 8, name, description, required|
);
map.serialize(serializer)
}
}
#[derive(Serialize)]
pub struct ArgumentChoice<T> {
pub name: &'static str,
pub value: T,
}