use crate::{
ArgumentParam,
BuilderError,
};
use serenity::model::application::{
CommandDataOptionValue,
CommandInteraction,
};
#[derive(Debug, thiserror::Error)]
pub enum ConvertError {
#[error("unexpected type for '{name}', expected '{expected}', got '{actual:?}'")]
UnexpectedType {
name: &'static str,
expected: DataType,
actual: Option<DataType>,
},
#[error("missing required field for '{name}', expected '{expected}'")]
MissingRequiredField {
name: &'static str,
expected: DataType,
},
}
pub trait FromOptions: std::fmt::Debug + Send
where
Self: Sized,
{
fn from_options(interaction: &CommandInteraction) -> Result<Self, ConvertError>;
fn get_argument_params() -> Result<Vec<ArgumentParam>, BuilderError> {
Ok(Vec::new())
}
}
impl FromOptions for () {
fn from_options(_interaction: &CommandInteraction) -> Result<Self, ConvertError> {
Ok(())
}
}
#[derive(Debug, Copy, Clone)]
pub enum DataType {
String,
Integer,
Boolean,
}
impl DataType {
pub fn as_str(self) -> &'static str {
match self {
Self::String => "String",
Self::Integer => "i64",
Self::Boolean => "bool",
}
}
pub fn from_data_option_value(v: &CommandDataOptionValue) -> Option<Self> {
match v {
CommandDataOptionValue::String(_) => Some(DataType::String),
CommandDataOptionValue::Integer(_) => Some(DataType::Integer),
CommandDataOptionValue::Boolean(_) => Some(DataType::Boolean),
_ => None,
}
}
}
impl std::fmt::Display for DataType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_str().fmt(f)
}
}
pub trait FromOptionValue: Sized {
fn from_option_value(
name: &'static str,
option: &CommandDataOptionValue,
) -> Result<Self, ConvertError>;
fn get_expected_data_type() -> DataType;
fn get_missing_default() -> Option<Self> {
None
}
}
impl FromOptionValue for bool {
fn from_option_value(
name: &'static str,
option: &CommandDataOptionValue,
) -> Result<Self, ConvertError> {
let expected = Self::get_expected_data_type();
match option {
CommandDataOptionValue::Boolean(b) => Ok(*b),
t => Err(ConvertError::UnexpectedType {
name,
expected,
actual: DataType::from_data_option_value(t),
}),
}
}
fn get_expected_data_type() -> DataType {
DataType::Boolean
}
}
impl FromOptionValue for String {
fn from_option_value(
name: &'static str,
option: &CommandDataOptionValue,
) -> Result<Self, ConvertError> {
let expected = Self::get_expected_data_type();
match option {
CommandDataOptionValue::String(s) => Ok(s.clone()),
t => Err(ConvertError::UnexpectedType {
name,
expected,
actual: DataType::from_data_option_value(t),
}),
}
}
fn get_expected_data_type() -> DataType {
DataType::String
}
}
impl<T> FromOptionValue for Option<T>
where
T: FromOptionValue,
{
fn from_option_value(
name: &'static str,
option: &CommandDataOptionValue,
) -> Result<Self, ConvertError> {
T::from_option_value(name, option).map(Some)
}
fn get_missing_default() -> Option<Self> {
Some(None)
}
fn get_expected_data_type() -> DataType {
T::get_expected_data_type()
}
}