use super::{pop_string, InvalidBool, MissingAttachment, TooFewArguments};
use crate::serenity_prelude as serenity;
use std::marker::PhantomData;
#[macro_export]
macro_rules! pop_prefix_argument {
($target:ty, $args:expr, $attachment_id:expr, $ctx:expr, $msg:expr) => {{
use $crate::PopArgumentHack as _;
(&std::marker::PhantomData::<$target>).pop_from($args, $attachment_id, $ctx, $msg)
}};
}
#[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,
) -> Result<(&'a str, usize, Self), (Box<dyn std::error::Error + Send + Sync>, Option<String>)>;
}
#[doc(hidden)]
#[async_trait::async_trait]
pub trait PopArgumentHack<'a, T>: Sized {
async fn pop_from(
self,
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> Result<(&'a str, usize, T), (Box<dyn std::error::Error + Send + Sync>, Option<String>)>;
}
#[async_trait::async_trait]
impl<'a, T: serenity::ArgumentConvert + Send> PopArgumentHack<'a, T> for PhantomData<T>
where
T::Err: std::error::Error + Send + Sync + 'static,
{
async fn pop_from(
self,
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> Result<(&'a str, usize, T), (Box<dyn std::error::Error + Send + Sync>, Option<String>)>
{
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))
}
}
#[async_trait::async_trait]
impl<'a, T: PopArgument<'a> + Send + Sync> PopArgumentHack<'a, T> for &PhantomData<T> {
async fn pop_from(
self,
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> Result<(&'a str, usize, T), (Box<dyn std::error::Error + Send + Sync>, Option<String>)>
{
T::pop_from(args, attachment_index, ctx, msg).await
}
}
#[async_trait::async_trait]
impl<'a> PopArgumentHack<'a, bool> for &PhantomData<bool> {
async fn pop_from(
self,
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> Result<(&'a str, usize, bool), (Box<dyn std::error::Error + Send + Sync>, Option<String>)>
{
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> PopArgumentHack<'a, serenity::Attachment> for &PhantomData<serenity::Attachment> {
async fn pop_from(
self,
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> Result<
(&'a str, usize, serenity::Attachment),
(Box<dyn std::error::Error + Send + Sync>, Option<String>),
> {
let attachment = msg
.attachments
.get(attachment_index)
.ok_or_else(|| (MissingAttachment::default().into(), None))?
.clone();
Ok((args, attachment_index + 1, attachment))
}
}
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> PopArgumentHack<'a, $type> for &PhantomData<$type> {
async fn pop_from(
self,
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> Result<
(&'a str, usize, $type),
(Box<dyn std::error::Error + Send + Sync>, Option<String>),
> {
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);