use super::{pop_string, InvalidBool, MissingAttachment, TooFewArguments};
use crate::serenity_prelude as serenity;
pub(crate) type PopArgumentResult<'a, T> =
Result<(&'a str, usize, T), (Box<dyn std::error::Error + Send + Sync>, Option<String>)>;
#[async_trait::async_trait]
pub trait PopArgument<'a>: Sized {
async fn pop_from(
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> PopArgumentResult<'a, Self>;
}
#[async_trait::async_trait]
impl<'a> PopArgument<'a> for bool {
async fn pop_from(
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> PopArgumentResult<'a, Self> {
let (args, string) =
pop_string(args).map_err(|_| (TooFewArguments::default().into(), None))?;
let value = match string.to_ascii_lowercase().trim() {
"yes" | "y" | "true" | "t" | "1" | "enable" | "on" => true,
"no" | "n" | "false" | "f" | "0" | "disable" | "off" => false,
_ => return Err((InvalidBool::default().into(), Some(string))),
};
Ok((args.trim_start(), attachment_index, value))
}
}
#[async_trait::async_trait]
impl<'a> PopArgument<'a> for serenity::Attachment {
async fn pop_from(
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> PopArgumentResult<'a, Self> {
let attachment = msg
.attachments
.get(attachment_index)
.ok_or_else(|| (MissingAttachment::default().into(), None))?
.clone();
Ok((args, attachment_index + 1, attachment))
}
}
#[async_trait::async_trait]
impl<'a> PopArgument<'a> for String {
async fn pop_from(
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> PopArgumentResult<'a, Self> {
match pop_string(args) {
Ok((args, string)) => Ok((args, attachment_index, string)),
Err(err) => Err((err.into(), Some(args.into()))),
}
}
}
async fn pop_from_argumentconvert<'a, T>(
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> PopArgumentResult<'a, T>
where
T: serenity::ArgumentConvert + Send,
T::Err: std::error::Error + Send + Sync + 'static,
{
let (args, string) = pop_string(args).map_err(|_| (TooFewArguments::default().into(), None))?;
let object = T::convert(ctx, msg.guild_id, Some(msg.channel_id), &string)
.await
.map_err(|e| (e.into(), Some(string)))?;
Ok((args.trim_start(), attachment_index, object))
}
macro_rules! argumentconvert_pop_argument {
( $(
$( #[cfg(feature = $feature:literal)] )?
$type:ty,
)* ) => {
$(
$( #[cfg(feature = $feature)] )?
#[async_trait::async_trait]
impl<'a> PopArgument<'a> for $type {
async fn pop_from(
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> PopArgumentResult<'a, Self> {
pop_from_argumentconvert(args, attachment_index, ctx, msg).await
}
}
)*
}
}
argumentconvert_pop_argument! {
f32, f64,
u8, u16, u32, u64,
i8, i16, i32, i64,
serenity::Mention,
serenity::User, serenity::Member,
serenity::Message,
serenity::Channel, serenity::GuildChannel,
serenity::EmojiId, serenity::Emoji,
serenity::Role,
#[cfg(feature = "cache")]
serenity::GuildId,
#[cfg(feature = "cache")]
serenity::Guild,
}
macro_rules! snowflake_pop_argument {
($type:ty, $parse_fn:ident, $error_type:ident) => {
#[derive(Default, Debug)]
pub struct $error_type {
#[doc(hidden)]
pub __non_exhaustive: (),
}
impl std::error::Error for $error_type {}
impl std::fmt::Display for $error_type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(concat!(
"Enter a valid ",
stringify!($error_type),
" ID or a mention."
))
}
}
#[async_trait::async_trait]
impl<'a> PopArgument<'a> for $type {
async fn pop_from(
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> PopArgumentResult<'a, Self> {
let (args, string) =
pop_string(args).map_err(|_| (TooFewArguments::default().into(), None))?;
if let Some(parsed_id) = string
.parse()
.ok()
.or_else(|| serenity::utils::$parse_fn(&string))
{
Ok((args.trim_start(), attachment_index, parsed_id))
} else {
Err(($error_type::default().into(), Some(string)))
}
}
}
};
}
snowflake_pop_argument!(serenity::UserId, parse_user_mention, InvalidUserId);
snowflake_pop_argument!(serenity::ChannelId, parse_channel_mention, InvalidChannelId);
snowflake_pop_argument!(serenity::RoleId, parse_role_mention, InvalidRoleId);