From 5d38cd705ab581f1cf6ad3f49f9e9b4cf280ca49 Mon Sep 17 00:00:00 2001 From: jamesbt365 Date: Tue, 22 Oct 2024 17:19:43 +0100 Subject: [PATCH 1/2] Parse id arguments as their mentions where appropriate --- src/prefix_argument/argument_trait.rs | 86 ++++++++++++++++++++++++++- src/prefix_argument/mod.rs | 39 ++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/src/prefix_argument/argument_trait.rs b/src/prefix_argument/argument_trait.rs index d43f2959b2c5..336d324099b2 100644 --- a/src/prefix_argument/argument_trait.rs +++ b/src/prefix_argument/argument_trait.rs @@ -2,7 +2,10 @@ //! the auto-deref specialization emulation code to e.g. support more strings for bool parameters //! instead of the `FromStr` ones -use super::{pop_string, InvalidBool, MissingAttachment, TooFewArguments}; +use super::{ + pop_string, InvalidBool, InvalidChannelId, InvalidRoleId, InvalidUserId, MissingAttachment, + TooFewArguments, +}; use crate::serenity_prelude as serenity; use std::marker::PhantomData; @@ -135,3 +138,84 @@ impl<'a> PopArgumentHack<'a, serenity::Attachment> for &PhantomData PopArgumentHack<'a, serenity::UserId> for &PhantomData { + async fn pop_from( + self, + args: &'a str, + attachment_index: usize, + ctx: &serenity::Context, + msg: &serenity::Message, + ) -> Result< + (&'a str, usize, serenity::UserId), + (Box, Option), + > { + let (args, string) = + pop_string(args).map_err(|_| (TooFewArguments::default().into(), None))?; + + if let Some(user_id) = string + .parse() + .ok() + .or_else(|| serenity::utils::parse_user_mention(&string)) + { + Ok((args.trim_start(), attachment_index, user_id)) + } else { + Err((InvalidUserId::default().into(), Some(string))) + } + } +} + +#[async_trait::async_trait] +impl<'a> PopArgumentHack<'a, serenity::RoleId> for &PhantomData { + async fn pop_from( + self, + args: &'a str, + attachment_index: usize, + ctx: &serenity::Context, + msg: &serenity::Message, + ) -> Result< + (&'a str, usize, serenity::RoleId), + (Box, Option), + > { + let (args, string) = + pop_string(args).map_err(|_| (TooFewArguments::default().into(), None))?; + + if let Some(user_id) = string + .parse() + .ok() + .or_else(|| serenity::utils::parse_role_mention(&string)) + { + Ok((args.trim_start(), attachment_index, user_id)) + } else { + Err((InvalidRoleId::default().into(), Some(string))) + } + } +} + +#[async_trait::async_trait] +impl<'a> PopArgumentHack<'a, serenity::ChannelId> for &PhantomData { + async fn pop_from( + self, + args: &'a str, + attachment_index: usize, + ctx: &serenity::Context, + msg: &serenity::Message, + ) -> Result< + (&'a str, usize, serenity::ChannelId), + (Box, Option), + > { + let (args, string) = + pop_string(args).map_err(|_| (TooFewArguments::default().into(), None))?; + + if let Some(user_id) = string + .parse() + .ok() + .or_else(|| serenity::utils::parse_channel_mention(&string)) + { + Ok((args.trim_start(), attachment_index, user_id)) + } else { + Err((InvalidChannelId::default().into(), Some(string))) + } + } +} diff --git a/src/prefix_argument/mod.rs b/src/prefix_argument/mod.rs index fdfa6e534712..991c240ab8f0 100644 --- a/src/prefix_argument/mod.rs +++ b/src/prefix_argument/mod.rs @@ -125,6 +125,45 @@ impl std::fmt::Display for InvalidBool { } impl std::error::Error for InvalidBool {} +/// Error thrown when the user enters a string that cannot be parsed as a UserId. +#[derive(Default, Debug)] +pub struct InvalidUserId { + #[doc(hidden)] + pub __non_exhaustive: (), +} +impl std::fmt::Display for InvalidUserId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Enter a valid user's ID or a mention.") + } +} +impl std::error::Error for InvalidUserId {} + +/// Error thrown when the user enters a string that cannot be parsed as a RoleId. +#[derive(Default, Debug)] +pub struct InvalidRoleId { + #[doc(hidden)] + pub __non_exhaustive: (), +} +impl std::fmt::Display for InvalidRoleId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Enter a valid role's ID or a mention.") + } +} +impl std::error::Error for InvalidRoleId {} + +/// Error thrown when the user enters a string that cannot be parsed as a RoleId. +#[derive(Default, Debug)] +pub struct InvalidChannelId { + #[doc(hidden)] + pub __non_exhaustive: (), +} +impl std::fmt::Display for InvalidChannelId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Enter a valid channel's ID or a mention.") + } +} +impl std::error::Error for InvalidChannelId {} + #[cfg(test)] #[test] fn test_pop_string() { From 533571b0a9d2a55a9c09e515259080c8c841b18a Mon Sep 17 00:00:00 2001 From: jamesbt365 Date: Tue, 22 Oct 2024 20:20:19 +0100 Subject: [PATCH 2/2] Write macro to deduplicate --- src/prefix_argument/argument_trait.rs | 126 ++++++++++---------------- src/prefix_argument/mod.rs | 39 -------- 2 files changed, 48 insertions(+), 117 deletions(-) diff --git a/src/prefix_argument/argument_trait.rs b/src/prefix_argument/argument_trait.rs index 336d324099b2..8a6bf6fee7a0 100644 --- a/src/prefix_argument/argument_trait.rs +++ b/src/prefix_argument/argument_trait.rs @@ -2,10 +2,7 @@ //! the auto-deref specialization emulation code to e.g. support more strings for bool parameters //! instead of the `FromStr` ones -use super::{ - pop_string, InvalidBool, InvalidChannelId, InvalidRoleId, InvalidUserId, MissingAttachment, - TooFewArguments, -}; +use super::{pop_string, InvalidBool, MissingAttachment, TooFewArguments}; use crate::serenity_prelude as serenity; use std::marker::PhantomData; @@ -139,83 +136,56 @@ impl<'a> PopArgumentHack<'a, serenity::Attachment> for &PhantomData PopArgumentHack<'a, serenity::UserId> for &PhantomData { - async fn pop_from( - self, - args: &'a str, - attachment_index: usize, - ctx: &serenity::Context, - msg: &serenity::Message, - ) -> Result< - (&'a str, usize, serenity::UserId), - (Box, Option), - > { - let (args, string) = - pop_string(args).map_err(|_| (TooFewArguments::default().into(), None))?; - - if let Some(user_id) = string - .parse() - .ok() - .or_else(|| serenity::utils::parse_user_mention(&string)) - { - Ok((args.trim_start(), attachment_index, user_id)) - } else { - Err((InvalidUserId::default().into(), Some(string))) +/// Macro to allow for using mentions in snowflake types +macro_rules! snowflake_pop_argument { + ($type:ty, $parse_fn:ident, $error_type:ident) => { + /// Error thrown when the user enters a string that cannot be parsed correctly. + #[derive(Default, Debug)] + pub struct $error_type { + #[doc(hidden)] + pub __non_exhaustive: (), } - } -} -#[async_trait::async_trait] -impl<'a> PopArgumentHack<'a, serenity::RoleId> for &PhantomData { - async fn pop_from( - self, - args: &'a str, - attachment_index: usize, - ctx: &serenity::Context, - msg: &serenity::Message, - ) -> Result< - (&'a str, usize, serenity::RoleId), - (Box, Option), - > { - let (args, string) = - pop_string(args).map_err(|_| (TooFewArguments::default().into(), None))?; - - if let Some(user_id) = string - .parse() - .ok() - .or_else(|| serenity::utils::parse_role_mention(&string)) - { - Ok((args.trim_start(), attachment_index, user_id)) - } else { - Err((InvalidRoleId::default().into(), Some(string))) + 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, serenity::ChannelId> for &PhantomData { - async fn pop_from( - self, - args: &'a str, - attachment_index: usize, - ctx: &serenity::Context, - msg: &serenity::Message, - ) -> Result< - (&'a str, usize, serenity::ChannelId), - (Box, Option), - > { - let (args, string) = - pop_string(args).map_err(|_| (TooFewArguments::default().into(), None))?; - if let Some(user_id) = string - .parse() - .ok() - .or_else(|| serenity::utils::parse_channel_mention(&string)) - { - Ok((args.trim_start(), attachment_index, user_id)) - } else { - Err((InvalidChannelId::default().into(), Some(string))) + #[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, Option), + > { + 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); diff --git a/src/prefix_argument/mod.rs b/src/prefix_argument/mod.rs index 991c240ab8f0..fdfa6e534712 100644 --- a/src/prefix_argument/mod.rs +++ b/src/prefix_argument/mod.rs @@ -125,45 +125,6 @@ impl std::fmt::Display for InvalidBool { } impl std::error::Error for InvalidBool {} -/// Error thrown when the user enters a string that cannot be parsed as a UserId. -#[derive(Default, Debug)] -pub struct InvalidUserId { - #[doc(hidden)] - pub __non_exhaustive: (), -} -impl std::fmt::Display for InvalidUserId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("Enter a valid user's ID or a mention.") - } -} -impl std::error::Error for InvalidUserId {} - -/// Error thrown when the user enters a string that cannot be parsed as a RoleId. -#[derive(Default, Debug)] -pub struct InvalidRoleId { - #[doc(hidden)] - pub __non_exhaustive: (), -} -impl std::fmt::Display for InvalidRoleId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("Enter a valid role's ID or a mention.") - } -} -impl std::error::Error for InvalidRoleId {} - -/// Error thrown when the user enters a string that cannot be parsed as a RoleId. -#[derive(Default, Debug)] -pub struct InvalidChannelId { - #[doc(hidden)] - pub __non_exhaustive: (), -} -impl std::fmt::Display for InvalidChannelId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("Enter a valid channel's ID or a mention.") - } -} -impl std::error::Error for InvalidChannelId {} - #[cfg(test)] #[test] fn test_pop_string() {