From 0a5a3d366bfafedc906ff893b743345d273d9a79 Mon Sep 17 00:00:00 2001 From: Victor Mignot Date: Thu, 2 Jan 2025 15:21:46 +0100 Subject: [PATCH] Implement the `Error` trait and improve our custom error --- src/discord/commands/commons.rs | 56 ++++++++++++---- src/discord/commands/create_tag.rs | 32 ++++------ src/discord/commands/delete_tag.rs | 32 ++++------ src/discord/commands/list_tags.rs | 24 ++----- src/discord/commands/source_code.rs | 18 ++---- src/discord/commands/subscribe.rs | 39 ++++------- src/discord/commands/tag_notify.rs | 45 ++++--------- src/discord/event_handler.rs | 8 +-- src/discord/message_builders/embed_builder.rs | 13 ++-- .../message_builders/selector_builder.rs | 64 +++++++------------ 10 files changed, 135 insertions(+), 196 deletions(-) diff --git a/src/discord/commands/commons.rs b/src/discord/commands/commons.rs index c6a2aa2..e0921f3 100644 --- a/src/discord/commands/commons.rs +++ b/src/discord/commands/commons.rs @@ -1,7 +1,7 @@ //! Commons elements that are used in while executing the bot commands. use log::{info, warn}; -use std::sync::Arc; +use std::{fmt::Display, sync::Arc}; use serenity::{ async_trait, @@ -18,31 +18,65 @@ use crate::database::Client as DatabaseClient; /// The kind of errors that can be returned while executing a command. #[derive(Debug)] -pub enum CommandExecutionError { +pub enum Error { /// An error that is returned when the `Serenity` crate failed to get the argument from the /// Discord API payload. - ArgumentExtractionError(String), + InvalidCommand(String), /// An error that is returned when the cast from the argument value to a Rust type failed. - ArgumentDeserializationError(String), + ArgumentDeserialization(String), /// The kind of error that is returned when a Database query failed. - DatabaseQueryError(String), + DatabaseQuery(String), /// Error returned when we fail to extract the context of a Discord commands. - ContextRetrievalError(String), + ContextRetrieval(String), /// Error returned when sending a command to the discord API failed. - DiscordAPICallError(String), + DiscordAPICall(serenity::Error), /// Error returned when there was an issue while using a selector embed in the command /// response. - SelectorError(String), + Selector(String), /// Error returned when the given command is unknown to Yorokobot. UnknownCommand(String), } +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let string = match self { + Error::InvalidCommand(e) => format!("Received an invalid command: {e}"), + Error::ArgumentDeserialization(e) => { + format!("Failed to deserialize command argument: {e}") + } + Error::DatabaseQuery(e) => format!("Failed to execute a database query: {e}"), + Error::ContextRetrieval(e) => { + format!("Failed to retrieve current session context: {e}") + } + Error::DiscordAPICall(e) => format!("Error during Discord API call: {e}"), + Error::Selector(e) => format!("Met an error while using an embed selector: {e}"), + Error::UnknownCommand(e) => format!("Met an unknown bot command: {e}"), + }; + + write!(f, "{string}") + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::InvalidCommand(_) => None, + Error::ArgumentDeserialization(_) => None, + Error::DatabaseQuery(_) => None, + Error::ContextRetrieval(_) => None, + Error::DiscordAPICall(error) => error.source(), + Error::Selector(_) => None, + Error::UnknownCommand(_) => None, + } + } +} + /// An option that can/have to be passed to a Discord command. pub struct BotCommandOption { pub name: String, @@ -67,11 +101,7 @@ pub trait BotCommand { fn options_list() -> Vec; /// Execute the command with the given `context` and using the given `database`. - async fn run( - &self, - context: Context, - database: Arc, - ) -> Result<(), CommandExecutionError>; + async fn run(&self, context: Context, database: Arc) -> Result<(), Error>; /// Extract and deserialize the `index`th value from the command `options`. fn extract_option_value( diff --git a/src/discord/commands/create_tag.rs b/src/discord/commands/create_tag.rs index b454fd1..3c38a3d 100644 --- a/src/discord/commands/create_tag.rs +++ b/src/discord/commands/create_tag.rs @@ -20,7 +20,7 @@ use crate::database::{ Client as DatabaseClient, }; -use super::commons::{BotCommand, BotCommandOption, CommandExecutionError}; +use super::commons::{BotCommand, BotCommandOption, Error}; pub struct CreateTagCommand { interaction: ApplicationCommandInteraction, @@ -50,25 +50,21 @@ impl BotCommand for CreateTagCommand { CreateTagCommand { interaction } } - async fn run( - &self, - context: Context, - database: Arc, - ) -> Result<(), CommandExecutionError> { + async fn run(&self, context: Context, database: Arc) -> Result<(), Error> { // Extract tag_name parameter let tag_name = match self.interaction.data.options.first() { Some(a) => match &a.resolved { Some(r) => match r { CommandDataOptionValue::String(r_str) => Ok(r_str), - _ => Err(CommandExecutionError::ArgumentDeserializationError( + _ => Err(Error::ArgumentDeserialization( "Received non String argument for the CreateTagCommand".to_string(), )), }, - None => Err(CommandExecutionError::ArgumentDeserializationError( + None => Err(Error::ArgumentDeserialization( "Could not deserialize the argument for the CreateTagCommand".to_string(), )), }, - None => Err(CommandExecutionError::ArgumentExtractionError( + None => Err(Error::InvalidCommand( "Failed to get the CreateTagCommand argument".to_string(), )), }?; @@ -76,7 +72,7 @@ impl BotCommand for CreateTagCommand { // Extract guild id from Serenity context let guild_id = match self.interaction.guild_id { Some(a) => Ok(a.to_string()), - None => Err(CommandExecutionError::ContextRetrievalError( + None => Err(Error::ContextRetrieval( "Could not fetch guild id from issued command".to_string(), )), }?; @@ -84,11 +80,11 @@ impl BotCommand for CreateTagCommand { let guild = match database.get_by_id::(&guild_id).await { Ok(query) => match query { Some(r) => Ok(r), - None => Err(CommandExecutionError::ContextRetrievalError( + None => Err(Error::ContextRetrieval( "Failed to retrieve the guild where the command was issued".to_string(), )), }, - Err(()) => Err(CommandExecutionError::DatabaseQueryError( + Err(()) => Err(Error::DatabaseQuery( "Could not access to the database".to_string(), )), }?; @@ -113,25 +109,19 @@ impl BotCommand for CreateTagCommand { .await { Ok(_) => Ok(String::from("Tag successfully created.")), - Err(_) => Err(CommandExecutionError::DatabaseQueryError( + Err(_) => Err(Error::DatabaseQuery( "Could not add new tag to the database".to_string(), )), }? }; - match self - .interaction + self.interaction .create_interaction_response(context.http, |response| { response .kind(InteractionResponseType::ChannelMessageWithSource) .interaction_response_data(|message| message.content(response_content)) }) .await - { - Ok(_) => Ok(()), - Err(_e) => Err(CommandExecutionError::DiscordAPICallError( - "Failed to answer to the initial command".to_string(), - )), - } + .map_err(Error::DiscordAPICall) } } diff --git a/src/discord/commands/delete_tag.rs b/src/discord/commands/delete_tag.rs index bb9dab2..60ddd11 100644 --- a/src/discord/commands/delete_tag.rs +++ b/src/discord/commands/delete_tag.rs @@ -18,7 +18,7 @@ use serenity::{ }; use super::{ - commons::{BotCommandOption, CommandExecutionError}, + commons::{BotCommandOption, Error}, BotCommand, }; @@ -50,31 +50,27 @@ impl BotCommand for DeleteTagCommand { }] } - async fn run( - &self, - context: Context, - database: Arc, - ) -> Result<(), CommandExecutionError> { + async fn run(&self, context: Context, database: Arc) -> Result<(), Error> { let tag_name = match self.interaction.data.options.first() { Some(a) => match &a.resolved { Some(r) => match r { CommandDataOptionValue::String(r_str) => Ok(r_str), - _ => Err(CommandExecutionError::ArgumentDeserializationError( + _ => Err(Error::ArgumentDeserialization( "Received non String argument for DeleteTagCommand".to_string(), )), }, - None => Err(CommandExecutionError::ArgumentDeserializationError( + None => Err(Error::ArgumentDeserialization( "Failed to deserialize argument for DeleteTagCommand".to_string(), )), }, - None => Err(CommandExecutionError::ArgumentExtractionError( + None => Err(Error::InvalidCommand( "Failed to find argument in DeleteTagCommand".to_string(), )), }?; let guild_id = match self.interaction.guild_id { Some(r) => Ok(r.to_string()), - None => Err(CommandExecutionError::ContextRetrievalError( + None => Err(Error::ContextRetrieval( "Failed to extract guild id from current context".to_string(), )), }?; @@ -82,11 +78,11 @@ impl BotCommand for DeleteTagCommand { let guild = match database.get_by_id::(&guild_id).await { Ok(query) => match query { Some(r) => Ok(r), - None => Err(CommandExecutionError::ContextRetrievalError( + None => Err(Error::ContextRetrieval( "Failed to retrieve the guild where the command was issued".to_string(), )), }, - Err(()) => Err(CommandExecutionError::DatabaseQueryError( + Err(()) => Err(Error::DatabaseQuery( "Failed to access to the database".to_string(), )), }?; @@ -104,7 +100,7 @@ impl BotCommand for DeleteTagCommand { .await { Ok(_) => Ok(String::from("Successfully remove the tag")), - Err(()) => Err(CommandExecutionError::DatabaseQueryError( + Err(()) => Err(Error::DatabaseQuery( "Failed to remove tag from the database".to_string(), )), }? @@ -112,18 +108,12 @@ impl BotCommand for DeleteTagCommand { String::from("No matching tag for this server.") }; - match self - .interaction + self.interaction .create_interaction_response(context.http, |r| { r.kind(InteractionResponseType::ChannelMessageWithSource) .interaction_response_data(|message| message.content(response)) }) .await - { - Ok(()) => Ok(()), - Err(_e) => Err(CommandExecutionError::DiscordAPICallError( - "Failed to answer the initial command".to_string(), - )), - } + .map_err(Error::DiscordAPICall) } } diff --git a/src/discord/commands/list_tags.rs b/src/discord/commands/list_tags.rs index faea1fc..c3bd397 100644 --- a/src/discord/commands/list_tags.rs +++ b/src/discord/commands/list_tags.rs @@ -13,7 +13,7 @@ use serenity::{ use crate::database::{models::Guild, Client as DatabaseClient}; use super::{ - commons::{BotCommandOption, CommandExecutionError}, + commons::{BotCommandOption, Error}, BotCommand, }; @@ -40,14 +40,10 @@ impl BotCommand for ListTagCommand { ListTagCommand { interaction } } - async fn run( - &self, - context: Context, - database: Arc, - ) -> Result<(), CommandExecutionError> { + async fn run(&self, context: Context, database: Arc) -> Result<(), Error> { let guild_id = match self.interaction.guild_id { Some(id) => Ok(id.to_string()), - None => Err(CommandExecutionError::ContextRetrievalError( + None => Err(Error::ContextRetrieval( "Failed to extract guild id from current context".to_string(), )), }?; @@ -55,11 +51,11 @@ impl BotCommand for ListTagCommand { let guild = match database.get_by_id::(&guild_id).await { Ok(query) => match query { Some(r) => Ok(r), - None => Err(CommandExecutionError::ContextRetrievalError( + None => Err(Error::ContextRetrieval( "Failed to retrieve the guild where the command was issued".to_string(), )), }, - Err(()) => Err(CommandExecutionError::DatabaseQueryError( + Err(()) => Err(Error::DatabaseQuery( "Failed to access to the database".to_string(), )), }?; @@ -79,19 +75,13 @@ impl BotCommand for ListTagCommand { } } - match self - .interaction + self.interaction .create_interaction_response(context.http, |response| { response .kind(InteractionResponseType::ChannelMessageWithSource) .interaction_response_data(|message| message.content(response_content)) }) .await - { - Ok(()) => Ok(()), - Err(_) => Err(CommandExecutionError::DiscordAPICallError( - "Failed to answer to the initial command".to_string(), - )), - } + .map_err(Error::DiscordAPICall) } } diff --git a/src/discord/commands/source_code.rs b/src/discord/commands/source_code.rs index daef252..645ef32 100644 --- a/src/discord/commands/source_code.rs +++ b/src/discord/commands/source_code.rs @@ -14,7 +14,7 @@ use crate::{ discord::message_builders::embed_builder::EmbedMessageBuilder, }; -use super::commons::{BotCommand, BotCommandOption, CommandExecutionError}; +use super::commons::{BotCommand, BotCommandOption, Error}; pub struct SourceCodeCommand { interaction: ApplicationCommandInteraction, @@ -39,14 +39,9 @@ impl BotCommand for SourceCodeCommand { SourceCodeCommand { interaction } } - async fn run( - &self, - context: Context, - _: Arc, - ) -> Result<(), CommandExecutionError> { + async fn run(&self, context: Context, _: Arc) -> Result<(), Error> { let embed_builder = EmbedMessageBuilder::new(&context).await?; - match self - .interaction + self.interaction .create_interaction_response(context.http, |response| { response .kind(InteractionResponseType::ChannelMessageWithSource) @@ -55,11 +50,6 @@ impl BotCommand for SourceCodeCommand { }) }) .await - { - Ok(()) => Ok(()), - Err(_e) => Err(CommandExecutionError::DiscordAPICallError( - "Failed to answer to the issued command".to_string(), - )), - } + .map_err(Error::DiscordAPICall) } } diff --git a/src/discord/commands/subscribe.rs b/src/discord/commands/subscribe.rs index cd7ae85..f396e1f 100644 --- a/src/discord/commands/subscribe.rs +++ b/src/discord/commands/subscribe.rs @@ -12,7 +12,7 @@ use serenity::{ }; use super::{ - commons::{BotCommandOption, CommandExecutionError}, + commons::{BotCommandOption, Error}, BotCommand, }; use crate::{ @@ -43,26 +43,18 @@ impl BotCommand for SubscribeCommand { vec![] } - async fn run( - &self, - context: Context, - database: Arc, - ) -> Result<(), CommandExecutionError> { + async fn run(&self, context: Context, database: Arc) -> Result<(), Error> { self.interaction .create_interaction_response(&context.http, |response| { response.kind(InteractionResponseType::DeferredChannelMessageWithSource) }) .await - .map_err(|_e| { - CommandExecutionError::DiscordAPICallError( - "Failed to answer with a temporary response".to_string(), - ) - })?; + .map_err(Error::DiscordAPICall)?; let user_id = self.interaction.user.id.to_string(); let guild_id = match self.interaction.guild_id { Some(id) => Ok(id.to_string()), - None => Err(CommandExecutionError::ContextRetrievalError( + None => Err(Error::ContextRetrieval( "Failed to extract guild id from current context".to_string(), )), }?; @@ -70,11 +62,11 @@ impl BotCommand for SubscribeCommand { let guild = match database.get_by_id::(&guild_id).await { Ok(query) => match query { Some(r) => Ok(r), - None => Err(CommandExecutionError::ContextRetrievalError( + None => Err(Error::ContextRetrieval( "Failed to retrieve the guild where the command was issued".to_string(), )), }, - Err(()) => Err(CommandExecutionError::DatabaseQueryError( + Err(()) => Err(Error::DatabaseQuery( "Failed to access to the database".to_string(), )), }?; @@ -117,7 +109,7 @@ impl BotCommand for SubscribeCommand { ) .await .map_err(|_| { - CommandExecutionError::DatabaseQueryError( + Error::DatabaseQuery( "Failed to update user subscriptions in database".to_string(), ) })?; @@ -125,25 +117,20 @@ impl BotCommand for SubscribeCommand { let mut response = match self.interaction.get_interaction_response(&context).await { Ok(r) => Ok(r), - Err(_e) => Err(CommandExecutionError::ContextRetrievalError( + Err(_e) => Err(Error::ContextRetrieval( "Failed to fetch initial interaction response".to_string(), )), }?; - response.delete_reactions(&context).await.map_err(|_e| { - CommandExecutionError::DiscordAPICallError( - "Failed to remove reactions from initial response".to_string(), - ) - })?; + response + .delete_reactions(&context) + .await + .map_err(Error::DiscordAPICall)?; response .edit(&context, |msg| msg.suppress_embeds(true).content("Done !")) .await - .map_err(|_e| { - CommandExecutionError::DiscordAPICallError( - "Failed to edit content of the original response message".to_string(), - ) - })?; + .map_err(Error::DiscordAPICall)?; Ok(()) } diff --git a/src/discord/commands/tag_notify.rs b/src/discord/commands/tag_notify.rs index dc77ca6..d716693 100644 --- a/src/discord/commands/tag_notify.rs +++ b/src/discord/commands/tag_notify.rs @@ -14,7 +14,7 @@ use serenity::{ }; use super::{ - commons::{BotCommandOption, CommandExecutionError}, + commons::{BotCommandOption, Error}, BotCommand, }; @@ -46,25 +46,17 @@ impl BotCommand for TagNotifyCommand { vec![] } - async fn run( - &self, - context: Context, - database: Arc, - ) -> Result<(), CommandExecutionError> { + async fn run(&self, context: Context, database: Arc) -> Result<(), Error> { self.interaction .create_interaction_response(&context.http, |response| { response.kind(InteractionResponseType::DeferredChannelMessageWithSource) }) .await - .map_err(|_e| { - CommandExecutionError::DiscordAPICallError( - "Failed to answer with a temporary response".to_string(), - ) - })?; + .map_err(Error::DiscordAPICall)?; let guild_id = match self.interaction.guild_id { Some(id) => Ok(id.to_string()), - None => Err(CommandExecutionError::ContextRetrievalError( + None => Err(Error::ContextRetrieval( "Failed to extract guild id from current context".to_string(), )), }?; @@ -72,11 +64,11 @@ impl BotCommand for TagNotifyCommand { let guild = match database.get_by_id::(&guild_id).await { Ok(query) => match query { Some(r) => Ok(r), - None => Err(CommandExecutionError::ContextRetrievalError( + None => Err(Error::ContextRetrieval( "Failed to retrieve the guild where the command was issued".to_string(), )), }, - Err(()) => Err(CommandExecutionError::DatabaseQueryError( + Err(()) => Err(Error::DatabaseQuery( "Failed to access to the database".to_string(), )), }?; @@ -104,7 +96,7 @@ impl BotCommand for TagNotifyCommand { for selected_tag in selection { let t = match guild.tags.iter().find(|s| s.name == selected_tag) { Some(t) => Ok(t), - None => Err(CommandExecutionError::ArgumentExtractionError( + None => Err(Error::InvalidCommand( "No matching tag found for selection".to_string(), )), }?; @@ -128,16 +120,15 @@ impl BotCommand for TagNotifyCommand { let response = match self.interaction.get_interaction_response(&context).await { Ok(r) => Ok(r), - Err(_e) => Err(CommandExecutionError::ContextRetrievalError( + Err(_e) => Err(Error::ContextRetrieval( "Failed to fetch initial interaction response".to_string(), )), }?; - response.delete(&context).await.map_err(|_e| { - CommandExecutionError::DiscordAPICallError( - "Failed to remove the embed message".to_string(), - ) - })?; + response + .delete(&context) + .await + .map_err(Error::DiscordAPICall)?; // We have to create a new message as editing the original response will not notify // the pinged users @@ -150,20 +141,12 @@ impl BotCommand for TagNotifyCommand { }) }) .await - .map_err(|_e| { - CommandExecutionError::DiscordAPICallError( - "Failed to create a new message to ping users".to_string(), - ) - })?; + .map_err(Error::DiscordAPICall)?; } else { self.interaction .delete_original_interaction_response(&context) .await - .map_err(|_e| { - CommandExecutionError::DiscordAPICallError( - "Failed to delete the original interaction message".to_string(), - ) - })?; + .map_err(Error::DiscordAPICall)?; } Ok(()) diff --git a/src/discord/event_handler.rs b/src/discord/event_handler.rs index 31efa91..898931f 100644 --- a/src/discord/event_handler.rs +++ b/src/discord/event_handler.rs @@ -8,7 +8,7 @@ use super::commands::{ use crate::{ database::{models::Guild, Client as DatabaseClient}, - discord::commands::{commons::CommandExecutionError, DeleteTagCommand, SubscribeCommand}, + discord::commands::{commons::Error, DeleteTagCommand, SubscribeCommand}, }; use serenity::{ async_trait, @@ -153,7 +153,7 @@ impl EventHandler for Handler { .await } else { drop(users_selector); - Err(CommandExecutionError::SelectorError( + Err(Error::Selector( "User has already a selector running".to_string(), )) } @@ -170,13 +170,13 @@ impl EventHandler for Handler { .await } else { drop(users_selector); - Err(CommandExecutionError::SelectorError( + Err(Error::Selector( "User has already a selector running".to_string(), )) } } - _ => Err(CommandExecutionError::UnknownCommand( + _ => Err(Error::UnknownCommand( "Received an unknown command from Discord".to_string(), )), }; diff --git a/src/discord/message_builders/embed_builder.rs b/src/discord/message_builders/embed_builder.rs index 65f2172..63994b6 100644 --- a/src/discord/message_builders/embed_builder.rs +++ b/src/discord/message_builders/embed_builder.rs @@ -5,7 +5,7 @@ use serenity::{ prelude::Context, }; -use crate::discord::commands::commons::CommandExecutionError; +use crate::discord::commands::commons::Error; const HTML_COLOR_CODE: u32 = 0xffffff; @@ -16,12 +16,11 @@ pub struct EmbedMessageBuilder { } impl EmbedMessageBuilder { - pub async fn new(context: &Context) -> Result { - let bot_user = context.http.get_current_user().await.map_err(|_e| { - CommandExecutionError::ContextRetrievalError( - "Failed to get current bot user".to_string(), - ) - })?; + pub async fn new(context: &Context) -> Result { + let bot_user = + context.http.get_current_user().await.map_err(|_e| { + Error::ContextRetrieval("Failed to get current bot user".to_string()) + })?; let embed_author = bot_user.name.clone(); diff --git a/src/discord/message_builders/selector_builder.rs b/src/discord/message_builders/selector_builder.rs index 3d804ca..f88e123 100644 --- a/src/discord/message_builders/selector_builder.rs +++ b/src/discord/message_builders/selector_builder.rs @@ -11,7 +11,7 @@ use serenity::{ prelude::Context, }; -use crate::discord::commands::commons::CommandExecutionError; +use crate::discord::commands::commons::Error; use super::embed_builder::EmbedMessageBuilder; @@ -83,9 +83,7 @@ impl<'a> EmbedSelector<'a> { selector } - pub async fn get_user_selection( - &mut self, - ) -> Result>, CommandExecutionError> { + pub async fn get_user_selection(&mut self) -> Result>, Error> { let embed_builder = EmbedMessageBuilder::new(self.context).await?; match self @@ -107,19 +105,16 @@ impl<'a> EmbedSelector<'a> { self.display_reactions().await?; Ok(self.wait_selector_end().await?) } - Err(_e) => Err(CommandExecutionError::DiscordAPICallError( - "Failed to edit original interaction response".to_string(), - )), + Err(e) => Err(Error::DiscordAPICall(e)), } } - async fn display_reactions(&self) -> Result<(), CommandExecutionError> { + async fn display_reactions(&self) -> Result<(), Error> { if let Some(answer) = &self.embed_answer { - answer.delete_reactions(self.context).await.map_err(|_e| { - CommandExecutionError::DiscordAPICallError( - "Failed to delete reaction on the current selector".to_string(), - ) - })?; + answer + .delete_reactions(self.context) + .await + .map_err(Error::DiscordAPICall)?; for emote in SELECTION_EMOTES[0..self.get_current_page_choice_number()] .iter() @@ -137,30 +132,24 @@ impl<'a> EmbedSelector<'a> { Some(a) => a .react(self.context, ReactionType::Unicode(emote.to_string())) .await - .map_err(|_e| { - CommandExecutionError::DiscordAPICallError( - "Failed to add reactions on the current selector".to_string(), - ) - }), - None => Err(CommandExecutionError::SelectorError( + .map_err(Error::DiscordAPICall), + None => Err(Error::Selector( "Failed to refresh the reactions of the current selector".to_string(), )), }?; } Ok(()) } else { - Err(CommandExecutionError::SelectorError( + Err(Error::Selector( "Tried to delete reaction from a non existent message".to_string(), )) } } - async fn wait_selector_end( - &mut self, - ) -> Result>, CommandExecutionError> { + async fn wait_selector_end(&mut self) -> Result>, Error> { let answer = match &self.embed_answer { Some(a) => Ok(a), - None => Err(CommandExecutionError::SelectorError( + None => Err(Error::Selector( "Tried to start collector before sending it".to_string(), )), }?; @@ -171,11 +160,7 @@ impl<'a> EmbedSelector<'a> { .add_message_id(*answer.id.as_u64()) .timeout(Duration::from_secs(COLLECTOR_MAX_DURATION_SEC)) .build() - .map_err(|_e| { - CommandExecutionError::SelectorError( - "Failed to build the EventCollector".to_string(), - ) - })?; + .map_err(|_e| Error::Selector("Failed to build the EventCollector".to_string()))?; while let Some(reaction_event) = collector.next().await { let reaction = match *reaction_event { @@ -232,11 +217,10 @@ impl<'a> EmbedSelector<'a> { } } - reaction.delete(self.context).await.map_err(|_e| { - CommandExecutionError::DiscordAPICallError( - "Failed to delete reaction from selector".to_string(), - ) - })?; + reaction + .delete(self.context) + .await + .map_err(Error::DiscordAPICall)?; } } collector.stop(); @@ -248,7 +232,7 @@ impl<'a> EmbedSelector<'a> { } } - async fn next_page(&mut self) -> Result<(), CommandExecutionError> { + async fn next_page(&mut self) -> Result<(), Error> { if self.current_page != self.page_number { self.current_page += 1; self.refresh_embed_selection().await?; @@ -256,7 +240,7 @@ impl<'a> EmbedSelector<'a> { Ok(()) } - async fn previous_page(&mut self) -> Result<(), CommandExecutionError> { + async fn previous_page(&mut self) -> Result<(), Error> { if self.current_page != 1 { self.current_page -= 1; self.refresh_embed_selection().await?; @@ -274,7 +258,7 @@ impl<'a> EmbedSelector<'a> { } } - async fn refresh_embed_selection(&mut self) -> Result<(), CommandExecutionError> { + async fn refresh_embed_selection(&mut self) -> Result<(), Error> { let embed_builder = EmbedMessageBuilder::new(self.context).await?; let curr_choices = self.selectable @@ -295,10 +279,6 @@ impl<'a> EmbedSelector<'a> { )) }) .await - .map_err(|_e| { - CommandExecutionError::DiscordAPICallError( - "Failed to edit selector content".to_string(), - ) - }) + .map_err(Error::DiscordAPICall) } }