diff --git a/twilight-gateway/src/event.rs b/twilight-gateway/src/event.rs index ea6e1212afa..9f3b3a7e47e 100644 --- a/twilight-gateway/src/event.rs +++ b/twilight-gateway/src/event.rs @@ -1,32 +1,33 @@ -//! Home of [`EventTypeFlags`], and optimization technique for skipping gateway -//! event deserialization. +//! Optimization for skipping deserialization of unwanted events. use bitflags::bitflags; -use twilight_model::gateway::{event::EventType, OpCode}; +use twilight_model::gateway::{event::DispatchEventType, OpCode}; bitflags! { /// Important optimization for narrowing requested event types. /// /// Specifying event types is an important optimization technique on top of - /// [intents], which can dramatically decrease processor usage in many + /// [`Intents`], which can dramatically decrease processor usage in many /// circumstances. While specifying intents are required by Discord and - /// allow filtering groups of [events], event type flags are a - /// Twilight-specific technique to filter out individual events from being - /// deserialized at all, effectively discarding those events. + /// allow filtering groups of [`Event`]s, event type flags are a Twilight + /// specific technique to filter out individual events from being + /// deserialized at all, effectively discarding them. /// /// For example, [`Intents::GUILDS`] includes a wide range of events from - /// [`GuildCreate`] to [`GuildRoleUpdate`] to [`ChannelPinsUpdate`]. If the - /// only events used in this group of events is, say, [`ChannelCreate`] and - /// [`GuildRoleCreate`], then the [`CHANNEL_CREATE`] and - /// [`GUILD_ROLE_CREATE`] event type flags can be specified in combination - /// with that intent. This reduces the events received and deserialized to - /// only those events. + /// [`GuildCreate`] to [`RoleUpdate`] to [`ChannelPinsUpdate`]. If the only + /// events used in this group of events is, say, [`ChannelCreate`] and + /// [`RoleCreate`], then the [`CHANNEL_CREATE`][Self::CHANNEL_CREATE] and + /// [`ROLE_CREATE`][Self::ROLE_CREATE] event type flags can be specified in + /// combination with that intent to only deserialize those events. /// - /// [`CHANNEL_CREATE`]: Self::CHANNEL_CREATE - /// [`GUILD_ROLE_CREATE`]: Self::GUILD_ROLE_CREATE - /// [`ChannelCreate`]: twilight_model::gateway::payload::incoming::ChannelCreate - /// [`ChannelPinsUpdate`]: twilight_model::gateway::payload::incoming::ChannelPinsUpdate - /// [`GuildRoleCreate`]: twilight_model::gateway::payload::incoming::GuildRoleCreate + /// [`ChannelCreate`]: twilight_model::gateway::event::Event::ChannelCreate + /// [`ChannelPinsUpdate`]: twilight_model::gateway::event::Event::ChannelPinsUpdate + /// [`Event`]: twilight_model::gateway::event::Event + /// [`GuildCreate`]: twilight_model::gateway::event::Event::GuildCreate + /// [`Intents`]: twilight_model::gateway::Intents + /// [`Intents::GUILDS`]: twilight_model::gateway::Intents::GUILDS + /// [`RoleCreate`]: twilight_model::gateway::event::Event::RoleCreate + /// [`RoleUpdate`]: twilight_model::gateway::event::Event::RoleUpdate pub struct EventTypeFlags: u128 { /// Message has been blocked by AutoMod according to a rule. const AUTO_MODERATION_ACTION_EXECUTION = 1 << 71; @@ -311,96 +312,91 @@ bitflags! { } } -impl From for EventTypeFlags { - fn from(event_type: EventType) -> Self { +impl From for EventTypeFlags { + fn from(event_type: DispatchEventType) -> Self { match event_type { - EventType::AutoModerationActionExecution => Self::AUTO_MODERATION_ACTION_EXECUTION, - EventType::AutoModerationRuleCreate => Self::AUTO_MODERATION_RULE_CREATE, - EventType::AutoModerationRuleDelete => Self::AUTO_MODERATION_RULE_DELETE, - EventType::AutoModerationRuleUpdate => Self::AUTO_MODERATION_RULE_UPDATE, - EventType::BanAdd => Self::BAN_ADD, - EventType::BanRemove => Self::BAN_REMOVE, - EventType::ChannelCreate => Self::CHANNEL_CREATE, - EventType::ChannelDelete => Self::CHANNEL_DELETE, - EventType::ChannelPinsUpdate => Self::CHANNEL_PINS_UPDATE, - EventType::ChannelUpdate => Self::CHANNEL_UPDATE, - EventType::CommandPermissionsUpdate => Self::COMMAND_PERMISSIONS_UPDATE, - EventType::GatewayClose => Self::empty(), - EventType::GatewayHeartbeat => Self::GATEWAY_HEARTBEAT, - EventType::GatewayHeartbeatAck => Self::GATEWAY_HEARTBEAT_ACK, - EventType::GatewayHello => Self::GATEWAY_HELLO, - EventType::GatewayInvalidateSession => Self::GATEWAY_INVALIDATE_SESSION, - EventType::GatewayReconnect => Self::GATEWAY_RECONNECT, - EventType::GiftCodeUpdate => Self::GIFT_CODE_UPDATE, - EventType::GuildCreate => Self::GUILD_CREATE, - EventType::GuildDelete => Self::GUILD_DELETE, - EventType::GuildEmojisUpdate => Self::GUILD_EMOJIS_UPDATE, - EventType::GuildIntegrationsUpdate => Self::GUILD_INTEGRATIONS_UPDATE, - EventType::GuildScheduledEventCreate => Self::GUILD_SCHEDULED_EVENT_CREATE, - EventType::GuildScheduledEventDelete => Self::GUILD_SCHEDULED_EVENT_DELETE, - EventType::GuildScheduledEventUpdate => Self::GUILD_SCHEDULED_EVENT_UPDATE, - EventType::GuildScheduledEventUserAdd => Self::GUILD_SCHEDULED_EVENT_USER_ADD, - EventType::GuildScheduledEventUserRemove => Self::GUILD_SCHEDULED_EVENT_USER_REMOVE, - EventType::GuildStickersUpdate => Self::GUILD_STICKERS_UPDATE, - EventType::GuildUpdate => Self::GUILD_UPDATE, - EventType::IntegrationCreate => Self::INTEGRATION_CREATE, - EventType::IntegrationDelete => Self::INTEGRATION_DELETE, - EventType::IntegrationUpdate => Self::INTEGRATION_UPDATE, - EventType::InteractionCreate => Self::INTERACTION_CREATE, - EventType::InviteCreate => Self::INVITE_CREATE, - EventType::InviteDelete => Self::INVITE_DELETE, - EventType::MemberAdd => Self::MEMBER_ADD, - EventType::MemberRemove => Self::MEMBER_REMOVE, - EventType::MemberUpdate => Self::MEMBER_UPDATE, - EventType::MemberChunk => Self::MEMBER_CHUNK, - EventType::MessageCreate => Self::MESSAGE_CREATE, - EventType::MessageDelete => Self::MESSAGE_DELETE, - EventType::MessageDeleteBulk => Self::MESSAGE_DELETE_BULK, - EventType::MessageUpdate => Self::MESSAGE_UPDATE, - EventType::PresenceUpdate => Self::PRESENCE_UPDATE, - EventType::PresencesReplace => Self::PRESENCES_REPLACE, - EventType::ReactionAdd => Self::REACTION_ADD, - EventType::ReactionRemove => Self::REACTION_REMOVE, - EventType::ReactionRemoveAll => Self::REACTION_REMOVE_ALL, - EventType::ReactionRemoveEmoji => Self::REACTION_REMOVE_EMOJI, - EventType::Ready => Self::READY, - EventType::Resumed => Self::RESUMED, - EventType::RoleCreate => Self::ROLE_CREATE, - EventType::RoleDelete => Self::ROLE_DELETE, - EventType::RoleUpdate => Self::ROLE_UPDATE, - EventType::StageInstanceCreate => Self::STAGE_INSTANCE_CREATE, - EventType::StageInstanceDelete => Self::STAGE_INSTANCE_DELETE, - EventType::StageInstanceUpdate => Self::STAGE_INSTANCE_UPDATE, - EventType::ThreadCreate => Self::THREAD_CREATE, - EventType::ThreadDelete => Self::THREAD_DELETE, - EventType::ThreadListSync => Self::THREAD_LIST_SYNC, - EventType::ThreadMembersUpdate => Self::THREAD_MEMBERS_UPDATE, - EventType::ThreadMemberUpdate => Self::THREAD_MEMBER_UPDATE, - EventType::ThreadUpdate => Self::THREAD_UPDATE, - EventType::TypingStart => Self::TYPING_START, - EventType::UnavailableGuild => Self::UNAVAILABLE_GUILD, - EventType::UserUpdate => Self::USER_UPDATE, - EventType::VoiceServerUpdate => Self::VOICE_SERVER_UPDATE, - EventType::VoiceStateUpdate => Self::VOICE_STATE_UPDATE, - EventType::WebhooksUpdate => Self::WEBHOOKS_UPDATE, + DispatchEventType::AutoModerationActionExecution => { + Self::AUTO_MODERATION_ACTION_EXECUTION + } + DispatchEventType::AutoModerationRuleCreate => Self::AUTO_MODERATION_RULE_CREATE, + DispatchEventType::AutoModerationRuleDelete => Self::AUTO_MODERATION_RULE_DELETE, + DispatchEventType::AutoModerationRuleUpdate => Self::AUTO_MODERATION_RULE_UPDATE, + DispatchEventType::BanAdd => Self::BAN_ADD, + DispatchEventType::BanRemove => Self::BAN_REMOVE, + DispatchEventType::ChannelCreate => Self::CHANNEL_CREATE, + DispatchEventType::ChannelDelete => Self::CHANNEL_DELETE, + DispatchEventType::ChannelPinsUpdate => Self::CHANNEL_PINS_UPDATE, + DispatchEventType::ChannelUpdate => Self::CHANNEL_UPDATE, + DispatchEventType::CommandPermissionsUpdate => Self::COMMAND_PERMISSIONS_UPDATE, + DispatchEventType::GiftCodeUpdate => Self::GIFT_CODE_UPDATE, + DispatchEventType::GuildCreate => Self::GUILD_CREATE, + DispatchEventType::GuildDelete => Self::GUILD_DELETE, + DispatchEventType::GuildEmojisUpdate => Self::GUILD_EMOJIS_UPDATE, + DispatchEventType::GuildIntegrationsUpdate => Self::GUILD_INTEGRATIONS_UPDATE, + DispatchEventType::GuildScheduledEventCreate => Self::GUILD_SCHEDULED_EVENT_CREATE, + DispatchEventType::GuildScheduledEventDelete => Self::GUILD_SCHEDULED_EVENT_DELETE, + DispatchEventType::GuildScheduledEventUpdate => Self::GUILD_SCHEDULED_EVENT_UPDATE, + DispatchEventType::GuildScheduledEventUserAdd => Self::GUILD_SCHEDULED_EVENT_USER_ADD, + DispatchEventType::GuildScheduledEventUserRemove => { + Self::GUILD_SCHEDULED_EVENT_USER_REMOVE + } + DispatchEventType::GuildStickersUpdate => Self::GUILD_STICKERS_UPDATE, + DispatchEventType::GuildUpdate => Self::GUILD_UPDATE, + DispatchEventType::IntegrationCreate => Self::INTEGRATION_CREATE, + DispatchEventType::IntegrationDelete => Self::INTEGRATION_DELETE, + DispatchEventType::IntegrationUpdate => Self::INTEGRATION_UPDATE, + DispatchEventType::InteractionCreate => Self::INTERACTION_CREATE, + DispatchEventType::InviteCreate => Self::INVITE_CREATE, + DispatchEventType::InviteDelete => Self::INVITE_DELETE, + DispatchEventType::MemberAdd => Self::MEMBER_ADD, + DispatchEventType::MemberRemove => Self::MEMBER_REMOVE, + DispatchEventType::MemberUpdate => Self::MEMBER_UPDATE, + DispatchEventType::MemberChunk => Self::MEMBER_CHUNK, + DispatchEventType::MessageCreate => Self::MESSAGE_CREATE, + DispatchEventType::MessageDelete => Self::MESSAGE_DELETE, + DispatchEventType::MessageDeleteBulk => Self::MESSAGE_DELETE_BULK, + DispatchEventType::MessageUpdate => Self::MESSAGE_UPDATE, + DispatchEventType::PresenceUpdate => Self::PRESENCE_UPDATE, + DispatchEventType::PresencesReplace => Self::PRESENCES_REPLACE, + DispatchEventType::ReactionAdd => Self::REACTION_ADD, + DispatchEventType::ReactionRemove => Self::REACTION_REMOVE, + DispatchEventType::ReactionRemoveAll => Self::REACTION_REMOVE_ALL, + DispatchEventType::ReactionRemoveEmoji => Self::REACTION_REMOVE_EMOJI, + DispatchEventType::Ready => Self::READY, + DispatchEventType::Resumed => Self::RESUMED, + DispatchEventType::RoleCreate => Self::ROLE_CREATE, + DispatchEventType::RoleDelete => Self::ROLE_DELETE, + DispatchEventType::RoleUpdate => Self::ROLE_UPDATE, + DispatchEventType::StageInstanceCreate => Self::STAGE_INSTANCE_CREATE, + DispatchEventType::StageInstanceDelete => Self::STAGE_INSTANCE_DELETE, + DispatchEventType::StageInstanceUpdate => Self::STAGE_INSTANCE_UPDATE, + DispatchEventType::ThreadCreate => Self::THREAD_CREATE, + DispatchEventType::ThreadDelete => Self::THREAD_DELETE, + DispatchEventType::ThreadListSync => Self::THREAD_LIST_SYNC, + DispatchEventType::ThreadMembersUpdate => Self::THREAD_MEMBERS_UPDATE, + DispatchEventType::ThreadMemberUpdate => Self::THREAD_MEMBER_UPDATE, + DispatchEventType::ThreadUpdate => Self::THREAD_UPDATE, + DispatchEventType::TypingStart => Self::TYPING_START, + DispatchEventType::UnavailableGuild => Self::UNAVAILABLE_GUILD, + DispatchEventType::UserUpdate => Self::USER_UPDATE, + DispatchEventType::VoiceServerUpdate => Self::VOICE_SERVER_UPDATE, + DispatchEventType::VoiceStateUpdate => Self::VOICE_STATE_UPDATE, + DispatchEventType::WebhooksUpdate => Self::WEBHOOKS_UPDATE, } } } -impl TryFrom<(OpCode, Option<&str>)> for EventTypeFlags { +impl TryFrom for EventTypeFlags { type Error = (); - fn try_from((op, event_type): (OpCode, Option<&str>)) -> Result { - match (op, event_type) { - (OpCode::Heartbeat, _) => Ok(Self::GATEWAY_HEARTBEAT), - (OpCode::Reconnect, _) => Ok(Self::GATEWAY_RECONNECT), - (OpCode::InvalidSession, _) => Ok(Self::GATEWAY_INVALIDATE_SESSION), - (OpCode::Hello, _) => Ok(Self::GATEWAY_HELLO), - (OpCode::HeartbeatAck, _) => Ok(Self::GATEWAY_HEARTBEAT_ACK), - (_, Some(event_type)) => EventType::try_from(event_type) - .map(Self::from) - .map_err(|_| ()), - (_, None) => Err(()), + fn try_from(op: OpCode) -> Result { + match op { + OpCode::Heartbeat => Ok(Self::GATEWAY_HEARTBEAT), + OpCode::Reconnect => Ok(Self::GATEWAY_RECONNECT), + OpCode::InvalidSession => Ok(Self::GATEWAY_INVALIDATE_SESSION), + OpCode::Hello => Ok(Self::GATEWAY_HELLO), + OpCode::HeartbeatAck => Ok(Self::GATEWAY_HEARTBEAT_ACK), + _ => Err(()), } } } @@ -410,17 +406,18 @@ mod tests { use super::EventTypeFlags; use static_assertions::assert_impl_all; use std::{fmt::Debug, hash::Hash}; - use twilight_model::gateway::event::EventType; + use twilight_model::gateway::{event::DispatchEventType, OpCode}; assert_impl_all!( EventTypeFlags: Copy, Clone, Debug, Eq, - From, + From, Hash, PartialEq, Send, Sync, + TryFrom, ); } diff --git a/twilight-gateway/src/json.rs b/twilight-gateway/src/json.rs index 635fe26d7ca..c4e9209310f 100644 --- a/twilight-gateway/src/json.rs +++ b/twilight-gateway/src/json.rs @@ -12,7 +12,7 @@ use crate::{ }; use serde::de::DeserializeSeed; use twilight_model::gateway::{ - event::{GatewayEvent, GatewayEventDeserializer}, + event::{DispatchEventType, GatewayEvent, GatewayEventDeserializer}, OpCode, }; @@ -43,18 +43,22 @@ pub fn parse( }); }; - let event_type = gateway_deserializer.event_type(); - - let event_flag = if let Ok(event_flag) = EventTypeFlags::try_from((opcode, event_type)) { - event_flag - } else { - let opcode = opcode as u8; - let source = format!("unknown opcode/dispatch event type: {opcode}/{event_type:?}"); - - return Err(ReceiveMessageError { - kind: ReceiveMessageErrorType::Deserializing { event: json }, - source: Some(source.into()), - }); + let event_flag = match gateway_deserializer + .event_type() + .map(str::parse::) + .transpose() + { + Ok(event_type) => opcode + .try_into() + .ok() + .or_else(|| event_type.map(Into::into)) + .unwrap_or(EventTypeFlags::empty()), + Err(source) => { + return Err(ReceiveMessageError { + kind: ReceiveMessageErrorType::Deserializing { event: json }, + source: Some(Box::new(source)), + }) + } }; if event_types.contains(event_flag) { diff --git a/twilight-gateway/src/lib.rs b/twilight-gateway/src/lib.rs index 446f387c6cf..4fb85206c54 100644 --- a/twilight-gateway/src/lib.rs +++ b/twilight-gateway/src/lib.rs @@ -39,6 +39,8 @@ mod session; mod shard; mod tls; +#[cfg(any(feature = "zlib-stock", feature = "zlib-simd"))] +pub use self::inflater::Inflater; pub use self::{ channel::MessageSender, command::Command, @@ -50,8 +52,6 @@ pub use self::{ session::Session, shard::{ConnectionStatus, Shard}, }; -#[cfg(any(feature = "zlib-stock", feature = "zlib-simd"))] -pub use inflater::Inflater; pub use twilight_model::gateway::{CloseFrame, Intents}; #[doc(no_inline)] diff --git a/twilight-model/Cargo.toml b/twilight-model/Cargo.toml index 4638f317626..b80a9aa6162 100644 --- a/twilight-model/Cargo.toml +++ b/twilight-model/Cargo.toml @@ -18,7 +18,7 @@ serde = { default-features = false, features = ["derive", "std"], version = "1.0 serde-value = { default-features = false, version = "0.7" } serde_repr = { default-features = false, version = "0.1.5" } time = { default-features = false, features = ["parsing", "std"], version = "0.3" } -tracing = { default-features = false, version = "0.1.16" } +tracing = { default-features = false, features = ["std", "attributes"], version = "0.1.16" } [dev-dependencies] criterion = { default-features = false, version = "0.3" } diff --git a/twilight-model/src/gateway/event/dispatch.rs b/twilight-model/src/gateway/event/dispatch.rs index d34510108c5..d8f57b831bf 100644 --- a/twilight-model/src/gateway/event/dispatch.rs +++ b/twilight-model/src/gateway/event/dispatch.rs @@ -1,151 +1,293 @@ -use super::{super::payload::incoming::*, Event, EventConversionError, EventType}; +use super::{Event, EventConversionError}; +use crate::{ + gateway::payload::incoming::*, + id::{marker::GuildMarker, Id}, +}; use serde::{ - de::{Deserialize, DeserializeSeed, Deserializer, Error as DeError, IgnoredAny}, - Serialize, + de::{value, DeserializeSeed, Deserializer, IgnoredAny, IntoDeserializer}, + Deserialize, Serialize, }; +use std::str::FromStr; -/// A dispatch event, containing information about a created guild, a member -/// added, etc. -/// -/// You can deserialize into a `DispatchEvent` via -/// [`DispatchEventWithTypeDeserializer`]. -// **NOTE**: When adding a variant, be sure to add it to the DeserializeSeed -// implementation. +/// Dispatch event data. #[derive(Clone, Debug, PartialEq, Serialize)] #[serde(untagged)] pub enum DispatchEvent { + /// Message was blocked by AutoMod according to a rule. AutoModerationActionExecution(AutoModerationActionExecution), + /// Sent when an auto moderation rule is created. AutoModerationRuleCreate(Box), + /// Sent when an auto moderation rule is deleted. AutoModerationRuleDelete(Box), + /// Sent when an auto moderation rule is updated. AutoModerationRuleUpdate(Box), + /// A user was banned from a guild. BanAdd(BanAdd), + /// A user's ban from a guild was removed. BanRemove(BanRemove), + /// A channel was created. ChannelCreate(Box), + /// A channel was deleted. ChannelDelete(Box), + /// A channel's pins were updated. ChannelPinsUpdate(ChannelPinsUpdate), + /// A channel was updated. ChannelUpdate(Box), + /// A command's permissions were updated. CommandPermissionsUpdate(CommandPermissionsUpdate), + /// Undocumented event, should be ignored. GiftCodeUpdate, + /// A guild was created. GuildCreate(Box), + /// A guild was deleted or the current user was removed from a guild. GuildDelete(GuildDelete), + /// A guild's emojis were updated. GuildEmojisUpdate(GuildEmojisUpdate), + /// A guild's integrations were updated. GuildIntegrationsUpdate(GuildIntegrationsUpdate), + /// A guild scheduled event was created. GuildScheduledEventCreate(Box), + /// A guild scheduled event was deleted. GuildScheduledEventDelete(Box), + /// A guild scheduled event was updated. GuildScheduledEventUpdate(Box), + /// A user was added to a guild scheduled event. GuildScheduledEventUserAdd(GuildScheduledEventUserAdd), + /// A user was removed from a guild scheduled event. GuildScheduledEventUserRemove(GuildScheduledEventUserRemove), + /// A guild's stickers were updated. GuildStickersUpdate(GuildStickersUpdate), + /// A guild was updated. GuildUpdate(Box), + /// A guild integration was created. IntegrationCreate(Box), + /// A guild integration was updated. IntegrationDelete(IntegrationDelete), + /// A guild integration was deleted. IntegrationUpdate(Box), + /// An interaction was invoked by a user. InteractionCreate(Box), + /// A invite was made. InviteCreate(Box), + /// A invite was deleted. InviteDelete(InviteDelete), + /// A user was added to a guild. MemberAdd(Box), + /// A user was removed from a guild. MemberRemove(MemberRemove), + /// A user's member object in a guild was updated. MemberUpdate(Box), + /// A chunk of members were received from the gateway. MemberChunk(MemberChunk), + /// A message was created in a channel. MessageCreate(Box), + /// A message was deleted in a channel. MessageDelete(MessageDelete), + /// Multiple messages were deleted in a channel. MessageDeleteBulk(MessageDeleteBulk), + /// A message was updated in a channel. MessageUpdate(Box), + /// A user's active presence (such as game or online status) was updated. PresenceUpdate(Box), + /// Multiple presences outside of a guild were updated. + /// + /// For bots this is always empty and useless. PresencesReplace, + /// A reaction was added to a message. ReactionAdd(Box), + /// A reaction was removed from a message. ReactionRemove(Box), + /// All reactions were removed from a message. ReactionRemoveAll(ReactionRemoveAll), + /// All instances of a given emoji from the reactions of a message were + /// removed. ReactionRemoveEmoji(ReactionRemoveEmoji), + /// A shard is now "ready" and fully connected. Ready(Box), + /// A shard has successfully resumed. Resumed, + /// A role was created in a guild. RoleCreate(RoleCreate), + /// A role was deleted in a guild. RoleDelete(RoleDelete), + /// A role was updated in a guild. RoleUpdate(RoleUpdate), + /// A stage instance was created in a stage channel. StageInstanceCreate(StageInstanceCreate), + /// A stage instance was deleted in a stage channel. StageInstanceDelete(StageInstanceDelete), + /// A stage instance was updated in a stage channel. StageInstanceUpdate(StageInstanceUpdate), + /// A thread has been created, relevant to the current user, + /// or the current user has been added to a thread. ThreadCreate(Box), + /// A thread, relevant to the current user, has been deleted. ThreadDelete(ThreadDelete), + /// The current user has gained access to a thread. ThreadListSync(ThreadListSync), + /// The thread member object for the current user has been updated. ThreadMemberUpdate(Box), + /// A user has been added to or removed from a thread. ThreadMembersUpdate(ThreadMembersUpdate), + /// A thread has been updated. ThreadUpdate(Box), + /// A user started typing in a channel. TypingStart(Box), + /// A guild is now unavailable. UnavailableGuild(UnavailableGuild), + /// The current user was updated. UserUpdate(UserUpdate), + /// A voice server update was sent. VoiceServerUpdate(VoiceServerUpdate), + /// A voice state in a voice channel was updated. VoiceStateUpdate(Box), + /// A webhook was updated. WebhooksUpdate(WebhooksUpdate), } impl DispatchEvent { - /// Returns the type of event that this event is. - pub const fn kind(&self) -> EventType { + /// ID of the guild from which the event originated, if known. + /// + /// Some events, such as [`MessageDelete`], will never include a guild ID, + /// others, such as [`BanAdd`], will always include one and some events, + /// such as [`ChannelCreate`] might include one. + pub const fn guild_id(&self) -> Option> { + match self { + Self::AutoModerationActionExecution(e) => Some(e.guild_id), + Self::AutoModerationRuleCreate(e) => Some(e.0.guild_id), + Self::AutoModerationRuleDelete(e) => Some(e.0.guild_id), + Self::AutoModerationRuleUpdate(e) => Some(e.0.guild_id), + Self::BanAdd(e) => Some(e.guild_id), + Self::BanRemove(e) => Some(e.guild_id), + Self::ChannelCreate(e) => e.0.guild_id, + Self::ChannelDelete(e) => e.0.guild_id, + Self::ChannelUpdate(e) => e.0.guild_id, + Self::CommandPermissionsUpdate(e) => Some(e.0.guild_id), + Self::GuildCreate(e) => Some(e.0.id), + Self::GuildDelete(e) => Some(e.id), + Self::GuildEmojisUpdate(e) => Some(e.guild_id), + Self::GuildIntegrationsUpdate(e) => Some(e.guild_id), + Self::GuildScheduledEventCreate(e) => Some(e.0.guild_id), + Self::GuildScheduledEventDelete(e) => Some(e.0.guild_id), + Self::GuildScheduledEventUpdate(e) => Some(e.0.guild_id), + Self::GuildScheduledEventUserAdd(e) => Some(e.guild_id), + Self::GuildScheduledEventUserRemove(e) => Some(e.guild_id), + Self::GuildStickersUpdate(e) => Some(e.guild_id), + Self::GuildUpdate(e) => Some(e.0.id), + Self::IntegrationCreate(e) => e.0.guild_id, + Self::IntegrationDelete(e) => Some(e.guild_id), + Self::IntegrationUpdate(e) => e.0.guild_id, + Self::InteractionCreate(e) => e.0.guild_id, + Self::InviteCreate(e) => Some(e.guild_id), + Self::InviteDelete(e) => Some(e.guild_id), + Self::MemberAdd(e) => Some(e.0.guild_id), + Self::MemberChunk(e) => Some(e.guild_id), + Self::MemberRemove(e) => Some(e.guild_id), + Self::MemberUpdate(e) => Some(e.guild_id), + Self::MessageCreate(e) => e.0.guild_id, + Self::PresenceUpdate(e) => Some(e.0.guild_id), + Self::ReactionAdd(e) => e.0.guild_id, + Self::ReactionRemove(e) => e.0.guild_id, + Self::ReactionRemoveAll(e) => e.guild_id, + Self::ReactionRemoveEmoji(e) => Some(e.guild_id), + Self::RoleCreate(e) => Some(e.guild_id), + Self::RoleDelete(e) => Some(e.guild_id), + Self::RoleUpdate(e) => Some(e.guild_id), + Self::StageInstanceCreate(e) => Some(e.0.guild_id), + Self::StageInstanceDelete(e) => Some(e.0.guild_id), + Self::StageInstanceUpdate(e) => Some(e.0.guild_id), + Self::ThreadCreate(e) => e.0.guild_id, + Self::ThreadDelete(e) => Some(e.guild_id), + Self::ThreadListSync(e) => Some(e.guild_id), + Self::ThreadMembersUpdate(e) => Some(e.guild_id), + Self::ThreadUpdate(e) => e.0.guild_id, + Self::TypingStart(e) => e.guild_id, + Self::UnavailableGuild(e) => Some(e.id), + Self::VoiceServerUpdate(e) => Some(e.guild_id), + Self::VoiceStateUpdate(e) => e.0.guild_id, + Self::WebhooksUpdate(e) => Some(e.guild_id), + Self::ChannelPinsUpdate(_) + | Self::GiftCodeUpdate + | Self::MessageDelete(_) + | Self::MessageDeleteBulk(_) + | Self::MessageUpdate(_) + | Self::PresencesReplace + | Self::Ready(_) + | Self::Resumed + | Self::ThreadMemberUpdate(_) + | Self::UserUpdate(_) => None, + } + } + /// Type of dispatch event. + pub const fn kind(&self) -> DispatchEventType { match self { - Self::AutoModerationActionExecution(_) => EventType::AutoModerationActionExecution, - Self::AutoModerationRuleCreate(_) => EventType::AutoModerationRuleCreate, - Self::AutoModerationRuleDelete(_) => EventType::AutoModerationRuleDelete, - Self::AutoModerationRuleUpdate(_) => EventType::AutoModerationRuleUpdate, - Self::BanAdd(_) => EventType::BanAdd, - Self::BanRemove(_) => EventType::BanRemove, - Self::ChannelCreate(_) => EventType::ChannelCreate, - Self::ChannelDelete(_) => EventType::ChannelDelete, - Self::ChannelPinsUpdate(_) => EventType::ChannelPinsUpdate, - Self::ChannelUpdate(_) => EventType::ChannelUpdate, - Self::CommandPermissionsUpdate(_) => EventType::CommandPermissionsUpdate, - Self::GiftCodeUpdate => EventType::GiftCodeUpdate, - Self::GuildCreate(_) => EventType::GuildCreate, - Self::GuildDelete(_) => EventType::GuildDelete, - Self::GuildEmojisUpdate(_) => EventType::GuildEmojisUpdate, - Self::GuildIntegrationsUpdate(_) => EventType::GuildIntegrationsUpdate, - Self::GuildScheduledEventCreate(_) => EventType::GuildScheduledEventCreate, - Self::GuildScheduledEventDelete(_) => EventType::GuildScheduledEventDelete, - Self::GuildScheduledEventUpdate(_) => EventType::GuildScheduledEventUpdate, - Self::GuildScheduledEventUserAdd(_) => EventType::GuildScheduledEventUserAdd, - Self::GuildScheduledEventUserRemove(_) => EventType::GuildScheduledEventUserRemove, - Self::GuildStickersUpdate(_) => EventType::GuildStickersUpdate, - Self::GuildUpdate(_) => EventType::GuildUpdate, - Self::IntegrationCreate(_) => EventType::IntegrationCreate, - Self::IntegrationDelete(_) => EventType::IntegrationDelete, - Self::IntegrationUpdate(_) => EventType::IntegrationUpdate, - Self::InteractionCreate(_) => EventType::InteractionCreate, - Self::InviteCreate(_) => EventType::InviteCreate, - Self::InviteDelete(_) => EventType::InviteDelete, - Self::MemberAdd(_) => EventType::MemberAdd, - Self::MemberRemove(_) => EventType::MemberRemove, - Self::MemberUpdate(_) => EventType::MemberUpdate, - Self::MemberChunk(_) => EventType::MemberChunk, - Self::MessageCreate(_) => EventType::MessageCreate, - Self::MessageDelete(_) => EventType::MessageDelete, - Self::MessageDeleteBulk(_) => EventType::MessageDeleteBulk, - Self::MessageUpdate(_) => EventType::MessageUpdate, - Self::PresenceUpdate(_) => EventType::PresenceUpdate, - Self::PresencesReplace => EventType::PresencesReplace, - Self::ReactionAdd(_) => EventType::ReactionAdd, - Self::ReactionRemove(_) => EventType::ReactionRemove, - Self::ReactionRemoveAll(_) => EventType::ReactionRemoveAll, - Self::ReactionRemoveEmoji(_) => EventType::ReactionRemoveEmoji, - Self::Ready(_) => EventType::Ready, - Self::Resumed => EventType::Resumed, - Self::RoleCreate(_) => EventType::RoleCreate, - Self::RoleDelete(_) => EventType::RoleDelete, - Self::RoleUpdate(_) => EventType::RoleUpdate, - Self::StageInstanceCreate(_) => EventType::StageInstanceCreate, - Self::StageInstanceDelete(_) => EventType::StageInstanceDelete, - Self::StageInstanceUpdate(_) => EventType::StageInstanceUpdate, - Self::ThreadCreate(_) => EventType::ThreadCreate, - Self::ThreadDelete(_) => EventType::ThreadDelete, - Self::ThreadListSync(_) => EventType::ThreadListSync, - Self::ThreadMemberUpdate(_) => EventType::ThreadMemberUpdate, - Self::ThreadMembersUpdate(_) => EventType::ThreadMembersUpdate, - Self::ThreadUpdate(_) => EventType::ThreadUpdate, - Self::TypingStart(_) => EventType::TypingStart, - Self::UnavailableGuild(_) => EventType::UnavailableGuild, - Self::UserUpdate(_) => EventType::UserUpdate, - Self::VoiceServerUpdate(_) => EventType::VoiceServerUpdate, - Self::VoiceStateUpdate(_) => EventType::VoiceStateUpdate, - Self::WebhooksUpdate(_) => EventType::WebhooksUpdate, + Self::AutoModerationActionExecution(_) => { + DispatchEventType::AutoModerationActionExecution + } + Self::AutoModerationRuleCreate(_) => DispatchEventType::AutoModerationRuleCreate, + Self::AutoModerationRuleDelete(_) => DispatchEventType::AutoModerationRuleDelete, + Self::AutoModerationRuleUpdate(_) => DispatchEventType::AutoModerationRuleUpdate, + Self::BanAdd(_) => DispatchEventType::BanAdd, + Self::BanRemove(_) => DispatchEventType::BanRemove, + Self::ChannelCreate(_) => DispatchEventType::ChannelCreate, + Self::ChannelDelete(_) => DispatchEventType::ChannelDelete, + Self::ChannelPinsUpdate(_) => DispatchEventType::ChannelPinsUpdate, + Self::ChannelUpdate(_) => DispatchEventType::ChannelUpdate, + Self::CommandPermissionsUpdate(_) => DispatchEventType::CommandPermissionsUpdate, + Self::GiftCodeUpdate => DispatchEventType::GiftCodeUpdate, + Self::GuildCreate(_) => DispatchEventType::GuildCreate, + Self::GuildDelete(_) => DispatchEventType::GuildDelete, + Self::GuildEmojisUpdate(_) => DispatchEventType::GuildEmojisUpdate, + Self::GuildIntegrationsUpdate(_) => DispatchEventType::GuildIntegrationsUpdate, + Self::GuildScheduledEventCreate(_) => DispatchEventType::GuildScheduledEventCreate, + Self::GuildScheduledEventDelete(_) => DispatchEventType::GuildScheduledEventDelete, + Self::GuildScheduledEventUpdate(_) => DispatchEventType::GuildScheduledEventUpdate, + Self::GuildScheduledEventUserAdd(_) => DispatchEventType::GuildScheduledEventUserAdd, + Self::GuildScheduledEventUserRemove(_) => { + DispatchEventType::GuildScheduledEventUserRemove + } + Self::GuildStickersUpdate(_) => DispatchEventType::GuildStickersUpdate, + Self::GuildUpdate(_) => DispatchEventType::GuildUpdate, + Self::IntegrationCreate(_) => DispatchEventType::IntegrationCreate, + Self::IntegrationDelete(_) => DispatchEventType::IntegrationDelete, + Self::IntegrationUpdate(_) => DispatchEventType::IntegrationUpdate, + Self::InteractionCreate(_) => DispatchEventType::InteractionCreate, + Self::InviteCreate(_) => DispatchEventType::InviteCreate, + Self::InviteDelete(_) => DispatchEventType::InviteDelete, + Self::MemberAdd(_) => DispatchEventType::MemberAdd, + Self::MemberRemove(_) => DispatchEventType::MemberRemove, + Self::MemberUpdate(_) => DispatchEventType::MemberUpdate, + Self::MemberChunk(_) => DispatchEventType::MemberChunk, + Self::MessageCreate(_) => DispatchEventType::MessageCreate, + Self::MessageDelete(_) => DispatchEventType::MessageDelete, + Self::MessageDeleteBulk(_) => DispatchEventType::MessageDeleteBulk, + Self::MessageUpdate(_) => DispatchEventType::MessageUpdate, + Self::PresenceUpdate(_) => DispatchEventType::PresenceUpdate, + Self::PresencesReplace => DispatchEventType::PresencesReplace, + Self::ReactionAdd(_) => DispatchEventType::ReactionAdd, + Self::ReactionRemove(_) => DispatchEventType::ReactionRemove, + Self::ReactionRemoveAll(_) => DispatchEventType::ReactionRemoveAll, + Self::ReactionRemoveEmoji(_) => DispatchEventType::ReactionRemoveEmoji, + Self::Ready(_) => DispatchEventType::Ready, + Self::Resumed => DispatchEventType::Resumed, + Self::RoleCreate(_) => DispatchEventType::RoleCreate, + Self::RoleDelete(_) => DispatchEventType::RoleDelete, + Self::RoleUpdate(_) => DispatchEventType::RoleUpdate, + Self::StageInstanceCreate(_) => DispatchEventType::StageInstanceCreate, + Self::StageInstanceDelete(_) => DispatchEventType::StageInstanceDelete, + Self::StageInstanceUpdate(_) => DispatchEventType::StageInstanceUpdate, + Self::ThreadCreate(_) => DispatchEventType::ThreadCreate, + Self::ThreadDelete(_) => DispatchEventType::ThreadDelete, + Self::ThreadListSync(_) => DispatchEventType::ThreadListSync, + Self::ThreadMemberUpdate(_) => DispatchEventType::ThreadMemberUpdate, + Self::ThreadMembersUpdate(_) => DispatchEventType::ThreadMembersUpdate, + Self::ThreadUpdate(_) => DispatchEventType::ThreadUpdate, + Self::TypingStart(_) => DispatchEventType::TypingStart, + Self::UnavailableGuild(_) => DispatchEventType::UnavailableGuild, + Self::UserUpdate(_) => DispatchEventType::UserUpdate, + Self::VoiceServerUpdate(_) => DispatchEventType::VoiceServerUpdate, + Self::VoiceStateUpdate(_) => DispatchEventType::VoiceStateUpdate, + Self::WebhooksUpdate(_) => DispatchEventType::WebhooksUpdate, } } } @@ -223,229 +365,491 @@ impl TryFrom for DispatchEvent { } } -/// Deserialize into a [`DispatchEvent`] by knowing its event name. -/// -/// An event name is something like `"CHANNEL_CREATE"` or `"GUILD_MEMBER_ADD"`. -#[derive(PartialEq, Eq)] -pub struct DispatchEventWithTypeDeserializer<'a>(&'a str); +/// Deserialize into a [`DispatchEvent`] by knowing its type. +#[derive(Debug)] +pub(crate) struct DispatchEventDeserializer(pub(crate) DispatchEventType); -impl<'a> DispatchEventWithTypeDeserializer<'a> { - /// Create a new deserializer. - pub const fn new(event_name: &'a str) -> Self { - Self(event_name) - } -} - -impl<'de, 'a> DeserializeSeed<'de> for DispatchEventWithTypeDeserializer<'a> { +impl<'de> DeserializeSeed<'de> for DispatchEventDeserializer { type Value = DispatchEvent; #[allow(clippy::too_many_lines)] fn deserialize>(self, deserializer: D) -> Result { Ok(match self.0 { - "AUTO_MODERATION_ACTION_EXECUTION" => DispatchEvent::AutoModerationActionExecution( - AutoModerationActionExecution::deserialize(deserializer)?, + DispatchEventType::AutoModerationActionExecution => { + DispatchEvent::AutoModerationActionExecution( + AutoModerationActionExecution::deserialize(deserializer)?, + ) + } + DispatchEventType::AutoModerationRuleCreate => DispatchEvent::AutoModerationRuleCreate( + Box::new(AutoModerationRuleCreate::deserialize(deserializer)?), ), - "AUTO_MODERATION_RULE_CREATE" => DispatchEvent::AutoModerationRuleCreate(Box::new( - AutoModerationRuleCreate::deserialize(deserializer)?, - )), - "AUTO_MODERATION_RULE_DELETE" => DispatchEvent::AutoModerationRuleDelete(Box::new( - AutoModerationRuleDelete::deserialize(deserializer)?, - )), - "AUTO_MODERATION_RULE_UPDATE" => DispatchEvent::AutoModerationRuleUpdate(Box::new( - AutoModerationRuleUpdate::deserialize(deserializer)?, - )), - "CHANNEL_CREATE" => { + DispatchEventType::AutoModerationRuleDelete => DispatchEvent::AutoModerationRuleDelete( + Box::new(AutoModerationRuleDelete::deserialize(deserializer)?), + ), + DispatchEventType::AutoModerationRuleUpdate => DispatchEvent::AutoModerationRuleUpdate( + Box::new(AutoModerationRuleUpdate::deserialize(deserializer)?), + ), + DispatchEventType::ChannelCreate => { DispatchEvent::ChannelCreate(Box::new(ChannelCreate::deserialize(deserializer)?)) } - "CHANNEL_DELETE" => { + DispatchEventType::ChannelDelete => { DispatchEvent::ChannelDelete(Box::new(ChannelDelete::deserialize(deserializer)?)) } - "CHANNEL_PINS_UPDATE" => { + DispatchEventType::ChannelPinsUpdate => { DispatchEvent::ChannelPinsUpdate(ChannelPinsUpdate::deserialize(deserializer)?) } - "CHANNEL_UPDATE" => { + DispatchEventType::ChannelUpdate => { DispatchEvent::ChannelUpdate(Box::new(ChannelUpdate::deserialize(deserializer)?)) } - "APPLICATION_COMMAND_PERMISSIONS_UPDATE" => DispatchEvent::CommandPermissionsUpdate( + DispatchEventType::CommandPermissionsUpdate => DispatchEvent::CommandPermissionsUpdate( CommandPermissionsUpdate::deserialize(deserializer)?, ), - "GIFT_CODE_UPDATE" => { + DispatchEventType::GiftCodeUpdate => { deserializer.deserialize_ignored_any(IgnoredAny)?; DispatchEvent::GiftCodeUpdate } - "GUILD_BAN_ADD" => DispatchEvent::BanAdd(BanAdd::deserialize(deserializer)?), - "GUILD_BAN_REMOVE" => DispatchEvent::BanRemove(BanRemove::deserialize(deserializer)?), - "GUILD_CREATE" => { + DispatchEventType::BanAdd => DispatchEvent::BanAdd(BanAdd::deserialize(deserializer)?), + DispatchEventType::BanRemove => { + DispatchEvent::BanRemove(BanRemove::deserialize(deserializer)?) + } + DispatchEventType::GuildCreate => { DispatchEvent::GuildCreate(Box::new(GuildCreate::deserialize(deserializer)?)) } - "GUILD_DELETE" => DispatchEvent::GuildDelete(GuildDelete::deserialize(deserializer)?), - "GUILD_EMOJIS_UPDATE" => { + DispatchEventType::GuildDelete => { + DispatchEvent::GuildDelete(GuildDelete::deserialize(deserializer)?) + } + DispatchEventType::GuildEmojisUpdate => { DispatchEvent::GuildEmojisUpdate(GuildEmojisUpdate::deserialize(deserializer)?) } - "GUILD_INTEGRATIONS_UPDATE" => DispatchEvent::GuildIntegrationsUpdate( + DispatchEventType::GuildIntegrationsUpdate => DispatchEvent::GuildIntegrationsUpdate( GuildIntegrationsUpdate::deserialize(deserializer)?, ), - "GUILD_SCHEDULED_EVENT_CREATE" => DispatchEvent::GuildScheduledEventCreate(Box::new( - GuildScheduledEventCreate::deserialize(deserializer)?, - )), - "GUILD_SCHEDULED_EVENT_DELETE" => DispatchEvent::GuildScheduledEventDelete(Box::new( - GuildScheduledEventDelete::deserialize(deserializer)?, - )), - "GUILD_SCHEDULED_EVENT_UPDATE" => DispatchEvent::GuildScheduledEventUpdate(Box::new( - GuildScheduledEventUpdate::deserialize(deserializer)?, - )), - "GUILD_SCHEDULED_EVENT_USER_ADD" => DispatchEvent::GuildScheduledEventUserAdd( - GuildScheduledEventUserAdd::deserialize(deserializer)?, - ), - "GUILD_SCHEDULED_EVENT_USER_REMOVE" => DispatchEvent::GuildScheduledEventUserRemove( - GuildScheduledEventUserRemove::deserialize(deserializer)?, - ), - "GUILD_MEMBERS_CHUNK" => { + DispatchEventType::GuildScheduledEventCreate => { + DispatchEvent::GuildScheduledEventCreate(Box::new( + GuildScheduledEventCreate::deserialize(deserializer)?, + )) + } + DispatchEventType::GuildScheduledEventDelete => { + DispatchEvent::GuildScheduledEventDelete(Box::new( + GuildScheduledEventDelete::deserialize(deserializer)?, + )) + } + DispatchEventType::GuildScheduledEventUpdate => { + DispatchEvent::GuildScheduledEventUpdate(Box::new( + GuildScheduledEventUpdate::deserialize(deserializer)?, + )) + } + DispatchEventType::GuildScheduledEventUserAdd => { + DispatchEvent::GuildScheduledEventUserAdd(GuildScheduledEventUserAdd::deserialize( + deserializer, + )?) + } + DispatchEventType::GuildScheduledEventUserRemove => { + DispatchEvent::GuildScheduledEventUserRemove( + GuildScheduledEventUserRemove::deserialize(deserializer)?, + ) + } + DispatchEventType::MemberChunk => { DispatchEvent::MemberChunk(MemberChunk::deserialize(deserializer)?) } - "GUILD_MEMBER_ADD" => { + DispatchEventType::MemberAdd => { DispatchEvent::MemberAdd(Box::new(MemberAdd::deserialize(deserializer)?)) } - "GUILD_MEMBER_REMOVE" => { + DispatchEventType::MemberRemove => { DispatchEvent::MemberRemove(MemberRemove::deserialize(deserializer)?) } - "GUILD_MEMBER_UPDATE" => { + DispatchEventType::MemberUpdate => { DispatchEvent::MemberUpdate(Box::new(MemberUpdate::deserialize(deserializer)?)) } - "GUILD_ROLE_CREATE" => { + DispatchEventType::RoleCreate => { DispatchEvent::RoleCreate(RoleCreate::deserialize(deserializer)?) } - "GUILD_ROLE_DELETE" => { + DispatchEventType::RoleDelete => { DispatchEvent::RoleDelete(RoleDelete::deserialize(deserializer)?) } - "GUILD_ROLE_UPDATE" => { + DispatchEventType::RoleUpdate => { DispatchEvent::RoleUpdate(RoleUpdate::deserialize(deserializer)?) } - "GUILD_STICKERS_UPDATE" => { + DispatchEventType::GuildStickersUpdate => { DispatchEvent::GuildStickersUpdate(GuildStickersUpdate::deserialize(deserializer)?) } - "GUILD_UPDATE" => { + DispatchEventType::GuildUpdate => { DispatchEvent::GuildUpdate(Box::new(GuildUpdate::deserialize(deserializer)?)) } - "INTEGRATION_CREATE" => DispatchEvent::IntegrationCreate(Box::new( + DispatchEventType::IntegrationCreate => DispatchEvent::IntegrationCreate(Box::new( IntegrationCreate::deserialize(deserializer)?, )), - "INTEGRATION_DELETE" => { + DispatchEventType::IntegrationDelete => { DispatchEvent::IntegrationDelete(IntegrationDelete::deserialize(deserializer)?) } - "INTEGRATION_UPDATE" => DispatchEvent::IntegrationUpdate(Box::new( + DispatchEventType::IntegrationUpdate => DispatchEvent::IntegrationUpdate(Box::new( IntegrationUpdate::deserialize(deserializer)?, )), - "INTERACTION_CREATE" => DispatchEvent::InteractionCreate(Box::new( + DispatchEventType::InteractionCreate => DispatchEvent::InteractionCreate(Box::new( InteractionCreate::deserialize(deserializer)?, )), - "INVITE_CREATE" => { + DispatchEventType::InviteCreate => { DispatchEvent::InviteCreate(Box::new(InviteCreate::deserialize(deserializer)?)) } - "INVITE_DELETE" => { + DispatchEventType::InviteDelete => { DispatchEvent::InviteDelete(InviteDelete::deserialize(deserializer)?) } - "MESSAGE_CREATE" => { + DispatchEventType::MessageCreate => { DispatchEvent::MessageCreate(Box::new(MessageCreate::deserialize(deserializer)?)) } - "MESSAGE_DELETE" => { + DispatchEventType::MessageDelete => { DispatchEvent::MessageDelete(MessageDelete::deserialize(deserializer)?) } - "MESSAGE_DELETE_BULK" => { + DispatchEventType::MessageDeleteBulk => { DispatchEvent::MessageDeleteBulk(MessageDeleteBulk::deserialize(deserializer)?) } - "MESSAGE_REACTION_ADD" => { + DispatchEventType::ReactionAdd => { DispatchEvent::ReactionAdd(Box::new(ReactionAdd::deserialize(deserializer)?)) } - "MESSAGE_REACTION_REMOVE" => { + DispatchEventType::ReactionRemove => { DispatchEvent::ReactionRemove(Box::new(ReactionRemove::deserialize(deserializer)?)) } - "MESSAGE_REACTION_REMOVE_EMOJI" => { + DispatchEventType::ReactionRemoveEmoji => { DispatchEvent::ReactionRemoveEmoji(ReactionRemoveEmoji::deserialize(deserializer)?) } - "MESSAGE_REACTION_REMOVE_ALL" => { + DispatchEventType::ReactionRemoveAll => { DispatchEvent::ReactionRemoveAll(ReactionRemoveAll::deserialize(deserializer)?) } - "MESSAGE_UPDATE" => { + DispatchEventType::MessageUpdate => { DispatchEvent::MessageUpdate(Box::new(MessageUpdate::deserialize(deserializer)?)) } - "PRESENCE_UPDATE" => { + DispatchEventType::PresenceUpdate => { DispatchEvent::PresenceUpdate(Box::new(PresenceUpdate::deserialize(deserializer)?)) } - "PRESENCES_REPLACE" => { + DispatchEventType::PresencesReplace => { deserializer.deserialize_ignored_any(IgnoredAny)?; DispatchEvent::PresencesReplace } - "READY" => DispatchEvent::Ready(Box::new(Ready::deserialize(deserializer)?)), - "RESUMED" => { + DispatchEventType::Ready => { + DispatchEvent::Ready(Box::new(Ready::deserialize(deserializer)?)) + } + DispatchEventType::Resumed => { deserializer.deserialize_ignored_any(IgnoredAny)?; DispatchEvent::Resumed } - "STAGE_INSTANCE_CREATE" => { + DispatchEventType::StageInstanceCreate => { DispatchEvent::StageInstanceCreate(StageInstanceCreate::deserialize(deserializer)?) } - "STAGE_INSTANCE_DELETE" => { + DispatchEventType::StageInstanceDelete => { DispatchEvent::StageInstanceDelete(StageInstanceDelete::deserialize(deserializer)?) } - "STAGE_INSTANCE_UPDATE" => { + DispatchEventType::StageInstanceUpdate => { DispatchEvent::StageInstanceUpdate(StageInstanceUpdate::deserialize(deserializer)?) } - "THREAD_CREATE" => { + DispatchEventType::ThreadCreate => { DispatchEvent::ThreadCreate(Box::new(ThreadCreate::deserialize(deserializer)?)) } - "THREAD_DELETE" => { + DispatchEventType::ThreadDelete => { DispatchEvent::ThreadDelete(ThreadDelete::deserialize(deserializer)?) } - "THREAD_LIST_SYNC" => { + DispatchEventType::ThreadListSync => { DispatchEvent::ThreadListSync(ThreadListSync::deserialize(deserializer)?) } - "THREAD_MEMBER_UPDATE" => DispatchEvent::ThreadMemberUpdate(Box::new( + DispatchEventType::ThreadMemberUpdate => DispatchEvent::ThreadMemberUpdate(Box::new( ThreadMemberUpdate::deserialize(deserializer)?, )), - "THREAD_MEMBERS_UPDATE" => { + DispatchEventType::ThreadMembersUpdate => { DispatchEvent::ThreadMembersUpdate(ThreadMembersUpdate::deserialize(deserializer)?) } - "THREAD_UPDATE" => { + DispatchEventType::ThreadUpdate => { DispatchEvent::ThreadUpdate(Box::new(ThreadUpdate::deserialize(deserializer)?)) } - "TYPING_START" => { + DispatchEventType::TypingStart => { DispatchEvent::TypingStart(Box::new(TypingStart::deserialize(deserializer)?)) } - "USER_UPDATE" => DispatchEvent::UserUpdate(UserUpdate::deserialize(deserializer)?), - "VOICE_SERVER_UPDATE" => { + DispatchEventType::UnavailableGuild => { + DispatchEvent::UnavailableGuild(UnavailableGuild::deserialize(deserializer)?) + } + DispatchEventType::UserUpdate => { + DispatchEvent::UserUpdate(UserUpdate::deserialize(deserializer)?) + } + DispatchEventType::VoiceServerUpdate => { DispatchEvent::VoiceServerUpdate(VoiceServerUpdate::deserialize(deserializer)?) } - "VOICE_STATE_UPDATE" => DispatchEvent::VoiceStateUpdate(Box::new( + DispatchEventType::VoiceStateUpdate => DispatchEvent::VoiceStateUpdate(Box::new( VoiceStateUpdate::deserialize(deserializer)?, )), - "WEBHOOKS_UPDATE" => { + DispatchEventType::WebhooksUpdate => { DispatchEvent::WebhooksUpdate(WebhooksUpdate::deserialize(deserializer)?) } - other => return Err(DeError::unknown_variant(other, &[])), }) } } +/// Type of a [`DispatchEvent`]. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum DispatchEventType { + AutoModerationActionExecution, + AutoModerationRuleCreate, + AutoModerationRuleDelete, + AutoModerationRuleUpdate, + #[serde(rename = "GUILD_BAN_ADD")] + BanAdd, + #[serde(rename = "GUILD_BAN_REMOVE")] + BanRemove, + ChannelCreate, + ChannelDelete, + ChannelPinsUpdate, + ChannelUpdate, + #[serde(rename = "APPLICATION_COMMAND_PERMISSIONS_UPDATE")] + CommandPermissionsUpdate, + GiftCodeUpdate, + GuildCreate, + GuildDelete, + GuildEmojisUpdate, + GuildIntegrationsUpdate, + GuildScheduledEventCreate, + GuildScheduledEventDelete, + GuildScheduledEventUpdate, + GuildScheduledEventUserAdd, + GuildScheduledEventUserRemove, + GuildStickersUpdate, + GuildUpdate, + IntegrationCreate, + IntegrationDelete, + IntegrationUpdate, + InteractionCreate, + InviteCreate, + InviteDelete, + #[serde(rename = "GUILD_MEMBER_ADD")] + MemberAdd, + #[serde(rename = "GUILD_MEMBERS_CHUNK")] + MemberChunk, + #[serde(rename = "GUILD_MEMBER_REMOVE")] + MemberRemove, + #[serde(rename = "GUILD_MEMBER_UPDATE")] + MemberUpdate, + MessageCreate, + MessageDelete, + MessageDeleteBulk, + MessageUpdate, + PresenceUpdate, + PresencesReplace, + #[serde(rename = "MESSAGE_REACTION_ADD")] + ReactionAdd, + #[serde(rename = "MESSAGE_REACTION_REMOVE")] + ReactionRemove, + #[serde(rename = "MESSAGE_REACTION_REMOVE_ALL")] + ReactionRemoveAll, + #[serde(rename = "MESSAGE_REACTION_REMOVE_EMOJI")] + ReactionRemoveEmoji, + Ready, + Resumed, + #[serde(rename = "GUILD_ROLE_CREATE")] + RoleCreate, + #[serde(rename = "GUILD_ROLE_DELETE")] + RoleDelete, + #[serde(rename = "GUILD_ROLE_UPDATE")] + RoleUpdate, + StageInstanceCreate, + StageInstanceDelete, + StageInstanceUpdate, + ThreadCreate, + ThreadDelete, + ThreadListSync, + ThreadMemberUpdate, + ThreadMembersUpdate, + ThreadUpdate, + TypingStart, + UnavailableGuild, + UserUpdate, + VoiceServerUpdate, + VoiceStateUpdate, + WebhooksUpdate, +} + +impl DispatchEventType { + /// Discord event name. + /// + /// Matches the [`Deserialize`] and [`Serialize`] implementations. + pub const fn name(self) -> &'static str { + match self { + Self::AutoModerationActionExecution => "AUTO_MODERATION_ACTION_EXECUTION", + Self::AutoModerationRuleCreate => "AUTO_MODERATION_RULE_CREATE", + Self::AutoModerationRuleDelete => "AUTO_MODERATION_RULE_DELETE", + Self::AutoModerationRuleUpdate => "AUTO_MODERATION_RULE_UPDATE", + Self::BanAdd => "GUILD_BAN_ADD", + Self::BanRemove => "GUILD_BAN_REMOVE", + Self::ChannelCreate => "CHANNEL_CREATE", + Self::ChannelDelete => "CHANNEL_DELETE", + Self::ChannelPinsUpdate => "CHANNEL_PINS_UPDATE", + Self::ChannelUpdate => "CHANNEL_UPDATE", + Self::CommandPermissionsUpdate => "APPLICATION_COMMAND_PERMISSIONS_UPDATE", + Self::GiftCodeUpdate => "GIFT_CODE_UPDATE", + Self::GuildCreate => "GUILD_CREATE", + Self::GuildDelete => "GUILD_DELETE", + Self::GuildEmojisUpdate => "GUILD_EMOJIS_UPDATE", + Self::GuildIntegrationsUpdate => "GUILD_INTEGRATIONS_UPDATE", + Self::GuildScheduledEventCreate => "GUILD_SCHEDULED_EVENT_CREATE", + Self::GuildScheduledEventDelete => "GUILD_SCHEDULED_EVENT_DELETE", + Self::GuildScheduledEventUpdate => "GUILD_SCHEDULED_EVENT_UPDATE", + Self::GuildScheduledEventUserAdd => "GUILD_SCHEDULED_EVENT_USER_ADD", + Self::GuildScheduledEventUserRemove => "GUILD_SCHEDULED_EVENT_USER_REMOVE", + Self::GuildStickersUpdate => "GUILD_STICKERS_UPDATE", + Self::GuildUpdate => "GUILD_UPDATE", + Self::IntegrationCreate => "INTEGRATION_CREATE", + Self::IntegrationDelete => "INTEGRATION_DELETE", + Self::IntegrationUpdate => "INTEGRATION_UPDATE", + Self::InteractionCreate => "INTERACTION_CREATE", + Self::InviteCreate => "INVITE_CREATE", + Self::InviteDelete => "INVITE_DELETE", + Self::MemberAdd => "GUILD_MEMBER_ADD", + Self::MemberChunk => "GUILD_MEMBERS_CHUNK", + Self::MemberRemove => "GUILD_MEMBER_REMOVE", + Self::MemberUpdate => "GUILD_MEMBER_UPDATE", + Self::MessageCreate => "MESSAGE_CREATE", + Self::MessageDelete => "MESSAGE_DELETE", + Self::MessageDeleteBulk => "MESSAGE_DELETE_BULK", + Self::MessageUpdate => "MESSAGE_UPDATE", + Self::PresencesReplace => "PRESENCES_REPLACE", + Self::PresenceUpdate => "PRESENCE_UPDATE", + Self::ReactionAdd => "MESSAGE_REACTION_ADD", + Self::ReactionRemove => "MESSAGE_REACTION_REMOVE", + Self::ReactionRemoveAll => "MESSAGE_REACTION_REMOVE_ALL", + Self::ReactionRemoveEmoji => "MESSAGE_REACTION_REMOVE_EMOJI", + Self::Ready => "READY", + Self::Resumed => "RESUMED", + Self::RoleCreate => "GUILD_ROLE_CREATE", + Self::RoleDelete => "GUILD_ROLE_DELETE", + Self::RoleUpdate => "GUILD_ROLE_UPDATE", + Self::StageInstanceCreate => "STAGE_INSTANCE_CREATE", + Self::StageInstanceDelete => "STAGE_INSTANCE_DELETE", + Self::StageInstanceUpdate => "STAGE_INSTANCE_UPDATE", + Self::ThreadCreate => "THREAD_CREATE", + Self::ThreadDelete => "THREAD_DELETE", + Self::ThreadListSync => "THREAD_LIST_SYNC", + Self::ThreadMembersUpdate => "THREAD_MEMBERS_UPDATE", + Self::ThreadMemberUpdate => "THREAD_MEMBER_UPDATE", + Self::ThreadUpdate => "THREAD_UPDATE", + Self::TypingStart => "TYPING_START", + Self::UnavailableGuild => "UNAVAILABLE_GUILD", + Self::UserUpdate => "USER_UPDATE", + Self::VoiceServerUpdate => "VOICE_SERVER_UPDATE", + Self::VoiceStateUpdate => "VOICE_STATE_UPDATE", + Self::WebhooksUpdate => "WEBHOOKS_UPDATE", + } + } +} + +impl FromStr for DispatchEventType { + type Err = value::Error; + + fn from_str(s: &str) -> Result { + Self::deserialize(s.into_deserializer()) + } +} + #[cfg(test)] mod tests { - use super::{DispatchEvent, DispatchEventWithTypeDeserializer}; - use serde::de::DeserializeSeed; - use serde_json::Deserializer; + //! [`EVENT_THRESHOLD`] is equivalent to 184 bytes. This was decided based + //! on the size of [`DispatchEvent`] at the time of writing. The assertions + //! here are to ensure that in the case the events themselves grow or shrink + //! past the threshold, they are properly boxed or unboxed respectively. + //! + //! If a field has been added to an event in the "unboxed" section and its + //! assertion now fails, then you will need to wrap the event in a box in + //! the `Event` type and move the assertion to the "boxed" section. + //! + //! Likewise, if a field has been removed from an event in the "boxed" + //! section and the assertion now fails, you will need to remove the box + //! wrapping the event in the `Event` type and move the assertion to the + //! "unboxed" section. - #[test] - fn gift_code_update() { - // Input will be ignored so long as it's valid JSON. - let input = r#"{ - "a": "b" - }"#; + use super::{DispatchEvent, DispatchEventType}; + use crate::gateway::payload::incoming::*; + use serde::{de::DeserializeOwned, Serialize}; + use static_assertions::{assert_impl_all, const_assert}; + use std::{fmt::Debug, hash::Hash, mem, str::FromStr}; - let deserializer = DispatchEventWithTypeDeserializer::new("GIFT_CODE_UPDATE"); - let mut json_deserializer = Deserializer::from_str(input); - let event = deserializer.deserialize(&mut json_deserializer).unwrap(); + assert_impl_all!( + DispatchEventType: Clone, + Copy, + Debug, + DeserializeOwned, + Eq, + FromStr, + Hash, + PartialEq, + Send, + Serialize, + Sync + ); - assert_eq!(event, DispatchEvent::GiftCodeUpdate); - } + // `dead_code`: `const_assert` operates at the compiler level, and the lint + // requires a variable to be used in a function, so this is a false + // positive. + #[allow(dead_code)] + const EVENT_THRESHOLD: usize = 184; + + const_assert!(mem::size_of::() == EVENT_THRESHOLD); + + // Boxed events. + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + + // Unboxed. + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); } diff --git a/twilight-model/src/gateway/event/gateway.rs b/twilight-model/src/gateway/event/gateway.rs index 1b5d1e19724..dc8063b27e2 100644 --- a/twilight-model/src/gateway/event/gateway.rs +++ b/twilight-model/src/gateway/event/gateway.rs @@ -1,11 +1,8 @@ -use super::{ - super::OpCode, DispatchEvent, DispatchEventWithTypeDeserializer, Event, EventConversionError, -}; -use crate::gateway::payload::incoming::Hello; +use super::{dispatch::DispatchEventDeserializer, DispatchEvent, EventType}; +use crate::gateway::{payload::incoming::Hello, OpCode}; use serde::{ de::{ - value::U8Deserializer, DeserializeSeed, Deserializer, Error as DeError, IgnoredAny, - IntoDeserializer, MapAccess, Unexpected, Visitor, + DeserializeSeed, Deserializer, Error as DeError, IgnoredAny, MapAccess, Unexpected, Visitor, }, ser::{SerializeStruct, Serializer}, Deserialize, Serialize, @@ -20,27 +17,45 @@ use std::{ /// stateful updates or a heartbeat, hello, etc. that a shard needs to operate. #[derive(Clone, Debug)] pub enum GatewayEvent { + /// Dispatch event sequence number and inner dispatch event. Dispatch(u64, DispatchEvent), + /// Heartbeat event, indicating that a heartbeat should be sent immediately. Heartbeat(u64), + /// Heartbeat acknowledgement. HeartbeatAck, + /// Hello event containinig heartbeat interval. Hello(Hello), + /// Shard's session was invalidated. + /// + /// `true` if resumable. If not, then the shard must do a full reconnect. InvalidateSession(bool), + /// Gateway indicating to permform a reconnect. Reconnect, } -impl TryFrom for GatewayEvent { - type Error = EventConversionError; - - fn try_from(event: Event) -> Result { - Ok(match event { - Event::GatewayHeartbeat(v) => Self::Heartbeat(v), - Event::GatewayHeartbeatAck => Self::HeartbeatAck, - Event::GatewayHello(v) => Self::Hello(v), - Event::GatewayInvalidateSession(v) => Self::InvalidateSession(v), - Event::GatewayReconnect => Self::Reconnect, +impl GatewayEvent { + /// Opcode of event. + pub const fn opcode(&self) -> OpCode { + match self { + Self::Dispatch(_, _) => OpCode::Dispatch, + Self::Heartbeat(_) => OpCode::Heartbeat, + Self::HeartbeatAck => OpCode::HeartbeatAck, + Self::Hello(_) => OpCode::Hello, + Self::InvalidateSession(_) => OpCode::InvalidSession, + Self::Reconnect => OpCode::Reconnect, + } + } - _ => return Err(EventConversionError::new(event)), - }) + /// Type of event. + pub fn kind(&self) -> EventType { + match self { + GatewayEvent::Dispatch(_, dispatch) => dispatch.kind().into(), + GatewayEvent::Heartbeat(_) => EventType::GatewayHeartbeat, + GatewayEvent::HeartbeatAck => EventType::GatewayHeartbeatAck, + GatewayEvent::Hello(_) => EventType::GatewayHello, + GatewayEvent::InvalidateSession(_) => EventType::GatewayInvalidateSession, + GatewayEvent::Reconnect => EventType::GatewayReconnect, + } } } @@ -180,15 +195,12 @@ impl<'a> GatewayEventDeserializer<'a> { struct GatewayEventVisitor<'a>(u8, Option, Option>); impl GatewayEventVisitor<'_> { - fn field<'de, T: Deserialize<'de>, V: MapAccess<'de>>( - map: &mut V, - field: Field, - ) -> Result { + fn data<'de, T: Deserialize<'de>, V: MapAccess<'de>>(map: &mut V) -> Result { let mut found = None; loop { match map.next_key::() { - Ok(Some(key)) if key == field => found = Some(map.next_value()?), + Ok(Some(key)) if key == Field::D => found = Some(map.next_value()?), Ok(Some(_)) | Err(_) => { map.next_value::()?; @@ -200,25 +212,14 @@ impl GatewayEventVisitor<'_> { } } - found.ok_or_else(|| { - DeError::missing_field(match field { - Field::D => "d", - Field::Op => "op", - Field::S => "s", - Field::T => "t", - }) - }) + found.ok_or_else(|| DeError::missing_field("d")) } fn ignore_all<'de, V: MapAccess<'de>>(map: &mut V) -> Result<(), V::Error> { - tracing::trace!("ignoring all other fields"); - while let Ok(Some(_)) | Err(_) = map.next_key::() { map.next_value::()?; } - tracing::trace!("ignored all other fields"); - Ok(()) } } @@ -231,12 +232,13 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { } #[allow(clippy::too_many_lines)] + #[tracing::instrument(level = "trace", name = "deserializing gateway event", skip_all)] fn visit_map(self, mut map: V) -> Result where V: MapAccess<'de>, { - static VALID_OPCODES: &[&str] = &[ - "EVENT", + const VALID_OPCODES: &[&str] = &[ + "DISPATCH", "HEARTBEAT", "HEARTBEAT_ACK", "HELLO", @@ -245,24 +247,19 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { "RECONNECT", ]; - let span = tracing::trace_span!("deserializing gateway event"); - let _span_enter = span.enter(); tracing::trace!(event_type=?self.2, op=self.0, seq=?self.1); - let op_deser: U8Deserializer = self.0.into_deserializer(); - - let op = OpCode::deserialize(op_deser).ok().ok_or_else(|| { - tracing::trace!(op = self.0, "unknown opcode"); - let unexpected = Unexpected::Unsigned(u64::from(self.0)); - - DeError::invalid_value(unexpected, &"an opcode") + let opcode = OpCode::from(self.0).ok_or_else(|| { + DeError::invalid_value(Unexpected::Unsigned(self.0.into()), &"an opcode") })?; - Ok(match op { + Ok(match opcode { OpCode::Dispatch => { let t = self .2 - .ok_or_else(|| DeError::custom("event type not provided beforehand"))?; + .ok_or_else(|| DeError::custom("event type not provided beforehand"))? + .parse() + .map_err(|_| todo!())?; tracing::trace!("deserializing gateway dispatch"); @@ -270,8 +267,7 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { let mut s = None; loop { - let span_child = tracing::trace_span!("iterating over element"); - let _span_child_enter = span_child.enter(); + let _span = tracing::trace_span!("iterating over element").entered(); let key = match map.next_key() { Ok(Some(key)) => { @@ -295,7 +291,7 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { return Err(DeError::duplicate_field("d")); } - let deserializer = DispatchEventWithTypeDeserializer::new(&t); + let deserializer = DispatchEventDeserializer(t); d = Some(map.next_value_seed(deserializer)?); } @@ -321,7 +317,7 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { } OpCode::Heartbeat => { tracing::trace!("deserializing gateway heartbeat"); - let seq = Self::field(&mut map, Field::D)?; + let seq = Self::data(&mut map)?; tracing::trace!(seq = %seq); Self::ignore_all(&mut map)?; @@ -337,7 +333,7 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { } OpCode::Hello => { tracing::trace!("deserializing gateway hello"); - let hello = Self::field::(&mut map, Field::D)?; + let hello = Self::data(&mut map)?; tracing::trace!(hello = ?hello); Self::ignore_all(&mut map)?; @@ -346,7 +342,7 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { } OpCode::InvalidSession => { tracing::trace!("deserializing invalid session"); - let invalidate = Self::field::(&mut map, Field::D)?; + let invalidate = Self::data(&mut map)?; tracing::trace!(invalidate = %invalidate); Self::ignore_all(&mut map)?; @@ -392,58 +388,40 @@ impl<'de> DeserializeSeed<'de> for GatewayEventDeserializer<'_> { impl Serialize for GatewayEvent { fn serialize(&self, serializer: S) -> Result { - const fn opcode(gateway_event: &GatewayEvent) -> OpCode { - match gateway_event { - GatewayEvent::Dispatch(_, _) => OpCode::Dispatch, - GatewayEvent::Heartbeat(_) => OpCode::Heartbeat, - GatewayEvent::HeartbeatAck => OpCode::HeartbeatAck, - GatewayEvent::Hello(_) => OpCode::Hello, - GatewayEvent::InvalidateSession(_) => OpCode::InvalidSession, - GatewayEvent::Reconnect => OpCode::Reconnect, - } - } - let mut s = serializer.serialize_struct("GatewayEvent", 4)?; - if let Self::Dispatch(sequence, event) = self { - s.serialize_field("t", &event.kind())?; - s.serialize_field("s", &sequence)?; - s.serialize_field("op", &opcode(self))?; - s.serialize_field("d", &event)?; - - return s.end(); - } - - // S and T are always null when not a Dispatch event - s.serialize_field("t", &None::<&str>)?; - s.serialize_field("s", &None::)?; - s.serialize_field("op", &opcode(self))?; - + s.serialize_field("op", &self.opcode())?; match self { - Self::Dispatch(_, _) => unreachable!("dispatch already handled"), - Self::Heartbeat(sequence) => { - s.serialize_field("d", &sequence)?; - } - Self::Hello(hello) => { - s.serialize_field("d", &hello)?; - } - Self::InvalidateSession(invalidate) => { - s.serialize_field("d", &invalidate)?; + GatewayEvent::Dispatch(seq, event) => { + s.serialize_field("d", event)?; + s.serialize_field("s", seq)?; + s.serialize_field("t", &event.kind())?; + + return s.end(); } - Self::HeartbeatAck | Self::Reconnect => { - s.serialize_field("d", &None::)?; + GatewayEvent::Heartbeat(seq) => s.serialize_field("d", seq)?, + GatewayEvent::Hello(hello) => s.serialize_field("d", hello)?, + GatewayEvent::InvalidateSession(resumable) => s.serialize_field("d", resumable)?, + GatewayEvent::HeartbeatAck | GatewayEvent::Reconnect => { + s.serialize_field("d", &None::<()>)? } } + s.serialize_field("s", &None::<()>)?; + s.serialize_field("t", &None::<()>)?; s.end() } } #[cfg(test)] mod tests { - use super::{DispatchEvent, GatewayEvent, GatewayEventDeserializer, OpCode}; + use super::{GatewayEvent, GatewayEventDeserializer}; use crate::{ - gateway::payload::incoming::{Hello, RoleDelete}, + gateway::{ + event::DispatchEvent, + payload::incoming::{Hello, RoleDelete}, + OpCode, + }, id::Id, test::image_hash, }; @@ -780,13 +758,6 @@ mod tests { name: "GatewayEvent", len: 4, }, - Token::Str("t"), - Token::UnitVariant { - name: "EventType", - variant: "GUILD_ROLE_DELETE", - }, - Token::Str("s"), - Token::U64(2_048), Token::Str("op"), Token::U8(OpCode::Dispatch as u8), Token::Str("d"), @@ -801,6 +772,13 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::StructEnd, + Token::Str("s"), + Token::U64(2_048), + Token::Str("t"), + Token::UnitVariant { + name: "DispatchEventType", + variant: "GUILD_ROLE_DELETE", + }, Token::StructEnd, ], ); @@ -815,14 +793,14 @@ mod tests { name: "GatewayEvent", len: 4, }, - Token::Str("t"), - Token::None, - Token::Str("s"), - Token::None, Token::Str("op"), Token::U8(OpCode::Heartbeat as u8), Token::Str("d"), Token::U64(1024), + Token::Str("s"), + Token::None, + Token::Str("t"), + Token::None, Token::StructEnd, ], ); @@ -837,14 +815,14 @@ mod tests { name: "GatewayEvent", len: 4, }, - Token::Str("t"), - Token::None, - Token::Str("s"), - Token::None, Token::Str("op"), Token::U8(OpCode::HeartbeatAck as u8), Token::Str("d"), Token::None, + Token::Str("s"), + Token::None, + Token::Str("t"), + Token::None, Token::StructEnd, ], ); @@ -861,10 +839,6 @@ mod tests { name: "GatewayEvent", len: 4, }, - Token::Str("t"), - Token::None, - Token::Str("s"), - Token::None, Token::Str("op"), Token::U8(OpCode::Hello as u8), Token::Str("d"), @@ -875,6 +849,10 @@ mod tests { Token::Str("heartbeat_interval"), Token::U64(41250), Token::StructEnd, + Token::Str("s"), + Token::None, + Token::Str("t"), + Token::None, Token::StructEnd, ], ); @@ -891,14 +869,14 @@ mod tests { name: "GatewayEvent", len: 4, }, - Token::Str("t"), - Token::None, - Token::Str("s"), - Token::None, Token::Str("op"), Token::U8(OpCode::InvalidSession as u8), Token::Str("d"), Token::Bool(true), + Token::Str("s"), + Token::None, + Token::Str("t"), + Token::None, Token::StructEnd, ], ); @@ -913,14 +891,14 @@ mod tests { name: "GatewayEvent", len: 4, }, - Token::Str("t"), - Token::None, - Token::Str("s"), - Token::None, Token::Str("op"), Token::U8(OpCode::Reconnect as u8), Token::Str("d"), Token::None, + Token::Str("s"), + Token::None, + Token::Str("t"), + Token::None, Token::StructEnd, ], ); diff --git a/twilight-model/src/gateway/event/kind.rs b/twilight-model/src/gateway/event/kind.rs index 5473c077ca3..250ce372670 100644 --- a/twilight-model/src/gateway/event/kind.rs +++ b/twilight-model/src/gateway/event/kind.rs @@ -1,6 +1,11 @@ +use super::DispatchEventType; use serde::{Deserialize, Serialize}; -/// The type of an event. +/// Type of an [`Event`]. +/// +/// The variants starting with `Gateway` are ignored by serde. +/// +/// [`Event`]: super::Event #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum EventType { @@ -18,11 +23,17 @@ pub enum EventType { ChannelUpdate, #[serde(rename = "APPLICATION_COMMAND_PERMISSIONS_UPDATE")] CommandPermissionsUpdate, + #[serde(skip)] GatewayClose, + #[serde(skip)] GatewayHeartbeat, + #[serde(skip)] GatewayHeartbeatAck, + #[serde(skip)] GatewayHello, + #[serde(skip)] GatewayInvalidateSession, + #[serde(skip)] GatewayReconnect, GiftCodeUpdate, GuildCreate, @@ -90,6 +101,9 @@ pub enum EventType { } impl EventType { + /// Discord event name. + /// + /// Returns [`None`] for the variants starting with `Gateway`. pub const fn name(self) -> Option<&'static str> { match self { Self::AutoModerationActionExecution => Some("AUTO_MODERATION_ACTION_EXECUTION"), @@ -103,6 +117,12 @@ impl EventType { Self::ChannelPinsUpdate => Some("CHANNEL_PINS_UPDATE"), Self::ChannelUpdate => Some("CHANNEL_UPDATE"), Self::CommandPermissionsUpdate => Some("APPLICATION_COMMAND_PERMISSIONS_UPDATE"), + Self::GatewayClose + | Self::GatewayHeartbeat + | Self::GatewayHeartbeatAck + | Self::GatewayHello + | Self::GatewayInvalidateSession + | Self::GatewayReconnect => None, Self::GiftCodeUpdate => Some("GIFT_CODE_UPDATE"), Self::GuildCreate => Some("GUILD_CREATE"), Self::GuildDelete => Some("GUILD_DELETE"), @@ -155,212 +175,214 @@ impl EventType { Self::VoiceServerUpdate => Some("VOICE_SERVER_UPDATE"), Self::VoiceStateUpdate => Some("VOICE_STATE_UPDATE"), Self::WebhooksUpdate => Some("WEBHOOKS_UPDATE"), - Self::GatewayClose - | Self::GatewayHeartbeat - | Self::GatewayHeartbeatAck - | Self::GatewayHello - | Self::GatewayInvalidateSession - | Self::GatewayReconnect => None, } } } -impl<'a> TryFrom<&'a str> for EventType { - type Error = &'a str; - - fn try_from(event_type: &'a str) -> Result { +impl From for EventType { + fn from(event_type: DispatchEventType) -> Self { match event_type { - "AUTO_MODERATION_ACTION_EXECUTION" => Ok(Self::AutoModerationActionExecution), - "AUTO_MODERATION_RULE_CREATE" => Ok(Self::AutoModerationRuleCreate), - "AUTO_MODERATION_RULE_DELETE" => Ok(Self::AutoModerationRuleDelete), - "AUTO_MODERATION_RULE_UPDATE" => Ok(Self::AutoModerationRuleUpdate), - "GUILD_BAN_ADD" => Ok(Self::BanAdd), - "GUILD_BAN_REMOVE" => Ok(Self::BanRemove), - "CHANNEL_CREATE" => Ok(Self::ChannelCreate), - "CHANNEL_DELETE" => Ok(Self::ChannelDelete), - "CHANNEL_PINS_UPDATE" => Ok(Self::ChannelPinsUpdate), - "CHANNEL_UPDATE" => Ok(Self::ChannelUpdate), - "APPLICATION_COMMAND_PERMISSIONS_UPDATE" => Ok(Self::CommandPermissionsUpdate), - "GIFT_CODE_UPDATE" => Ok(Self::GiftCodeUpdate), - "GUILD_CREATE" => Ok(Self::GuildCreate), - "GUILD_DELETE" => Ok(Self::GuildDelete), - "GUILD_EMOJIS_UPDATE" => Ok(Self::GuildEmojisUpdate), - "GUILD_INTEGRATIONS_UPDATE" => Ok(Self::GuildIntegrationsUpdate), - "GUILD_SCHEDULED_EVENT_CREATE" => Ok(Self::GuildScheduledEventCreate), - "GUILD_SCHEDULED_EVENT_DELETE" => Ok(Self::GuildScheduledEventDelete), - "GUILD_SCHEDULED_EVENT_UPDATE" => Ok(Self::GuildScheduledEventUpdate), - "GUILD_SCHEDULED_EVENT_USER_ADD" => Ok(Self::GuildScheduledEventUserAdd), - "GUILD_SCHEDULED_EVENT_USER_REMOVE" => Ok(Self::GuildScheduledEventUserRemove), - "GUILD_UPDATE" => Ok(Self::GuildUpdate), - "INTEGRATION_CREATE" => Ok(Self::IntegrationCreate), - "INTEGRATION_DELETE" => Ok(Self::IntegrationDelete), - "INTEGRATION_UPDATE" => Ok(Self::IntegrationUpdate), - "INTERACTION_CREATE" => Ok(Self::InteractionCreate), - "INVITE_CREATE" => Ok(Self::InviteCreate), - "INVITE_DELETE" => Ok(Self::InviteDelete), - "GUILD_MEMBER_ADD" => Ok(Self::MemberAdd), - "GUILD_MEMBER_REMOVE" => Ok(Self::MemberRemove), - "GUILD_MEMBER_UPDATE" => Ok(Self::MemberUpdate), - "GUILD_MEMBERS_CHUNK" => Ok(Self::MemberChunk), - "MESSAGE_CREATE" => Ok(Self::MessageCreate), - "MESSAGE_DELETE" => Ok(Self::MessageDelete), - "MESSAGE_DELETE_BULK" => Ok(Self::MessageDeleteBulk), - "MESSAGE_UPDATE" => Ok(Self::MessageUpdate), - "PRESENCE_UPDATE" => Ok(Self::PresenceUpdate), - "PRESENCES_REPLACE" => Ok(Self::PresencesReplace), - "MESSAGE_REACTION_ADD" => Ok(Self::ReactionAdd), - "MESSAGE_REACTION_REMOVE" => Ok(Self::ReactionRemove), - "MESSAGE_REACTION_REMOVE_ALL" => Ok(Self::ReactionRemoveAll), - "MESSAGE_REACTION_REMOVE_EMOJI" => Ok(Self::ReactionRemoveEmoji), - "READY" => Ok(Self::Ready), - "RESUMED" => Ok(Self::Resumed), - "GUILD_ROLE_CREATE" => Ok(Self::RoleCreate), - "GUILD_ROLE_DELETE" => Ok(Self::RoleDelete), - "GUILD_ROLE_UPDATE" => Ok(Self::RoleUpdate), - "STAGE_INSTANCE_CREATE" => Ok(Self::StageInstanceCreate), - "STAGE_INSTANCE_DELETE" => Ok(Self::StageInstanceDelete), - "STAGE_INSTANCE_UPDATE" => Ok(Self::StageInstanceUpdate), - "THREAD_CREATE" => Ok(Self::ThreadCreate), - "THREAD_DELETE" => Ok(Self::ThreadDelete), - "THREAD_LIST_SYNC" => Ok(Self::ThreadListSync), - "THREAD_MEMBER_UPDATE" => Ok(Self::ThreadMemberUpdate), - "THREAD_MEMBERS_UPDATE" => Ok(Self::ThreadMembersUpdate), - "THREAD_UPDATE" => Ok(Self::ThreadUpdate), - "TYPING_START" => Ok(Self::TypingStart), - "UNAVAILABLE_GUILD" => Ok(Self::UnavailableGuild), - "USER_UPDATE" => Ok(Self::UserUpdate), - "VOICE_SERVER_UPDATE" => Ok(Self::VoiceServerUpdate), - "VOICE_STATE_UPDATE" => Ok(Self::VoiceStateUpdate), - "WEBHOOKS_UPDATE" => Ok(Self::WebhooksUpdate), - _ => Err(event_type), + DispatchEventType::AutoModerationActionExecution => Self::AutoModerationActionExecution, + DispatchEventType::AutoModerationRuleCreate => Self::AutoModerationRuleCreate, + DispatchEventType::AutoModerationRuleDelete => Self::AutoModerationRuleDelete, + DispatchEventType::AutoModerationRuleUpdate => Self::AutoModerationRuleUpdate, + DispatchEventType::BanAdd => Self::BanAdd, + DispatchEventType::BanRemove => Self::BanRemove, + DispatchEventType::ChannelCreate => Self::ChannelCreate, + DispatchEventType::ChannelDelete => Self::ChannelDelete, + DispatchEventType::ChannelPinsUpdate => Self::ChannelPinsUpdate, + DispatchEventType::ChannelUpdate => Self::ChannelUpdate, + DispatchEventType::CommandPermissionsUpdate => Self::CommandPermissionsUpdate, + DispatchEventType::GiftCodeUpdate => Self::GiftCodeUpdate, + DispatchEventType::GuildCreate => Self::GuildCreate, + DispatchEventType::GuildDelete => Self::GuildDelete, + DispatchEventType::GuildEmojisUpdate => Self::GuildEmojisUpdate, + DispatchEventType::GuildIntegrationsUpdate => Self::GuildIntegrationsUpdate, + DispatchEventType::GuildScheduledEventCreate => Self::GuildScheduledEventCreate, + DispatchEventType::GuildScheduledEventDelete => Self::GuildScheduledEventDelete, + DispatchEventType::GuildScheduledEventUpdate => Self::GuildScheduledEventUpdate, + DispatchEventType::GuildScheduledEventUserAdd => Self::GuildScheduledEventUserAdd, + DispatchEventType::GuildScheduledEventUserRemove => Self::GuildScheduledEventUserRemove, + DispatchEventType::GuildStickersUpdate => Self::GuildStickersUpdate, + DispatchEventType::GuildUpdate => Self::GuildUpdate, + DispatchEventType::IntegrationCreate => Self::IntegrationCreate, + DispatchEventType::IntegrationDelete => Self::IntegrationDelete, + DispatchEventType::IntegrationUpdate => Self::IntegrationUpdate, + DispatchEventType::InteractionCreate => Self::InteractionCreate, + DispatchEventType::InviteCreate => Self::InviteCreate, + DispatchEventType::InviteDelete => Self::InviteDelete, + DispatchEventType::MemberAdd => Self::MemberAdd, + DispatchEventType::MemberChunk => Self::MemberChunk, + DispatchEventType::MemberRemove => Self::MemberRemove, + DispatchEventType::MemberUpdate => Self::MemberUpdate, + DispatchEventType::MessageCreate => Self::MessageCreate, + DispatchEventType::MessageDelete => Self::MessageDelete, + DispatchEventType::MessageDeleteBulk => Self::MessageDeleteBulk, + DispatchEventType::MessageUpdate => Self::MessageUpdate, + DispatchEventType::PresenceUpdate => Self::PresenceUpdate, + DispatchEventType::PresencesReplace => Self::PresencesReplace, + DispatchEventType::ReactionAdd => Self::ReactionAdd, + DispatchEventType::ReactionRemove => Self::ReactionRemove, + DispatchEventType::ReactionRemoveAll => Self::ReactionRemoveAll, + DispatchEventType::ReactionRemoveEmoji => Self::ReactionRemoveEmoji, + DispatchEventType::Ready => Self::Ready, + DispatchEventType::Resumed => Self::Resumed, + DispatchEventType::RoleCreate => Self::RoleCreate, + DispatchEventType::RoleDelete => Self::RoleDelete, + DispatchEventType::RoleUpdate => Self::RoleUpdate, + DispatchEventType::StageInstanceCreate => Self::StageInstanceCreate, + DispatchEventType::StageInstanceDelete => Self::StageInstanceDelete, + DispatchEventType::StageInstanceUpdate => Self::StageInstanceUpdate, + DispatchEventType::ThreadCreate => Self::ThreadCreate, + DispatchEventType::ThreadDelete => Self::ThreadDelete, + DispatchEventType::ThreadListSync => Self::ThreadListSync, + DispatchEventType::ThreadMemberUpdate => Self::ThreadMemberUpdate, + DispatchEventType::ThreadMembersUpdate => Self::ThreadMembersUpdate, + DispatchEventType::ThreadUpdate => Self::ThreadUpdate, + DispatchEventType::TypingStart => Self::TypingStart, + DispatchEventType::UnavailableGuild => Self::UnavailableGuild, + DispatchEventType::UserUpdate => Self::UserUpdate, + DispatchEventType::VoiceServerUpdate => Self::VoiceServerUpdate, + DispatchEventType::VoiceStateUpdate => Self::VoiceStateUpdate, + DispatchEventType::WebhooksUpdate => Self::WebhooksUpdate, } } } #[cfg(test)] mod tests { - use super::EventType; + use super::{super::DispatchEventType, EventType}; + use serde::{de::DeserializeOwned, Serialize}; use serde_test::Token; + use static_assertions::assert_impl_all; + use std::{fmt::Debug, hash::Hash}; - fn assert_variant(kind: EventType, name: &'static str) { - serde_test::assert_tokens( - &kind, - &[Token::UnitVariant { - name: "EventType", - variant: name, - }], - ); - } + assert_impl_all!( + EventType: Clone, + Copy, + Debug, + DeserializeOwned, + Eq, + From, + Hash, + PartialEq, + Send, + Serialize, + Sync + ); - #[allow(clippy::too_many_lines)] - #[test] - fn variants() { - assert_variant( + const MAP: &[(EventType, &str)] = &[ + ( EventType::AutoModerationActionExecution, "AUTO_MODERATION_ACTION_EXECUTION", - ); - assert_variant( + ), + ( EventType::AutoModerationRuleCreate, "AUTO_MODERATION_RULE_CREATE", - ); - assert_variant( + ), + ( EventType::AutoModerationRuleDelete, "AUTO_MODERATION_RULE_DELETE", - ); - assert_variant( + ), + ( EventType::AutoModerationRuleUpdate, "AUTO_MODERATION_RULE_UPDATE", - ); - assert_variant(EventType::BanAdd, "GUILD_BAN_ADD"); - assert_variant(EventType::BanRemove, "GUILD_BAN_REMOVE"); - assert_variant(EventType::ChannelCreate, "CHANNEL_CREATE"); - assert_variant(EventType::ChannelDelete, "CHANNEL_DELETE"); - assert_variant(EventType::ChannelPinsUpdate, "CHANNEL_PINS_UPDATE"); - assert_variant(EventType::ChannelUpdate, "CHANNEL_UPDATE"); - assert_variant( + ), + (EventType::BanAdd, "GUILD_BAN_ADD"), + (EventType::BanRemove, "GUILD_BAN_REMOVE"), + (EventType::ChannelCreate, "CHANNEL_CREATE"), + (EventType::ChannelDelete, "CHANNEL_DELETE"), + (EventType::ChannelPinsUpdate, "CHANNEL_PINS_UPDATE"), + (EventType::ChannelUpdate, "CHANNEL_UPDATE"), + ( EventType::CommandPermissionsUpdate, "APPLICATION_COMMAND_PERMISSIONS_UPDATE", - ); - assert_variant(EventType::GatewayClose, "GATEWAY_CLOSE"); - assert_variant(EventType::GatewayHeartbeat, "GATEWAY_HEARTBEAT"); - assert_variant(EventType::GatewayHeartbeatAck, "GATEWAY_HEARTBEAT_ACK"); - assert_variant(EventType::GatewayHello, "GATEWAY_HELLO"); - assert_variant( - EventType::GatewayInvalidateSession, - "GATEWAY_INVALIDATE_SESSION", - ); - assert_variant(EventType::GatewayReconnect, "GATEWAY_RECONNECT"); - assert_variant(EventType::GiftCodeUpdate, "GIFT_CODE_UPDATE"); - assert_variant(EventType::GuildCreate, "GUILD_CREATE"); - assert_variant(EventType::GuildDelete, "GUILD_DELETE"); - assert_variant(EventType::GuildEmojisUpdate, "GUILD_EMOJIS_UPDATE"); - assert_variant( + ), + (EventType::GiftCodeUpdate, "GIFT_CODE_UPDATE"), + (EventType::GuildCreate, "GUILD_CREATE"), + (EventType::GuildDelete, "GUILD_DELETE"), + (EventType::GuildEmojisUpdate, "GUILD_EMOJIS_UPDATE"), + ( EventType::GuildIntegrationsUpdate, "GUILD_INTEGRATIONS_UPDATE", - ); - assert_variant( + ), + ( EventType::GuildScheduledEventCreate, "GUILD_SCHEDULED_EVENT_CREATE", - ); - assert_variant( + ), + ( EventType::GuildScheduledEventDelete, "GUILD_SCHEDULED_EVENT_DELETE", - ); - assert_variant( + ), + ( EventType::GuildScheduledEventUpdate, "GUILD_SCHEDULED_EVENT_UPDATE", - ); - assert_variant( + ), + ( EventType::GuildScheduledEventUserAdd, "GUILD_SCHEDULED_EVENT_USER_ADD", - ); - assert_variant( + ), + ( EventType::GuildScheduledEventUserRemove, "GUILD_SCHEDULED_EVENT_USER_REMOVE", - ); - assert_variant(EventType::GuildUpdate, "GUILD_UPDATE"); - assert_variant(EventType::IntegrationCreate, "INTEGRATION_CREATE"); - assert_variant(EventType::IntegrationDelete, "INTEGRATION_DELETE"); - assert_variant(EventType::IntegrationUpdate, "INTEGRATION_UPDATE"); - assert_variant(EventType::InteractionCreate, "INTERACTION_CREATE"); - assert_variant(EventType::InviteCreate, "INVITE_CREATE"); - assert_variant(EventType::InviteDelete, "INVITE_DELETE"); - assert_variant(EventType::MemberAdd, "GUILD_MEMBER_ADD"); - assert_variant(EventType::MemberChunk, "GUILD_MEMBERS_CHUNK"); - assert_variant(EventType::MemberRemove, "GUILD_MEMBER_REMOVE"); - assert_variant(EventType::MemberUpdate, "GUILD_MEMBER_UPDATE"); - assert_variant(EventType::MessageCreate, "MESSAGE_CREATE"); - assert_variant(EventType::MessageDelete, "MESSAGE_DELETE"); - assert_variant(EventType::MessageDeleteBulk, "MESSAGE_DELETE_BULK"); - assert_variant(EventType::MessageUpdate, "MESSAGE_UPDATE"); - assert_variant(EventType::PresenceUpdate, "PRESENCE_UPDATE"); - assert_variant(EventType::PresencesReplace, "PRESENCES_REPLACE"); - assert_variant(EventType::ReactionAdd, "MESSAGE_REACTION_ADD"); - assert_variant(EventType::ReactionRemove, "MESSAGE_REACTION_REMOVE"); - assert_variant(EventType::ReactionRemoveAll, "MESSAGE_REACTION_REMOVE_ALL"); - assert_variant( + ), + (EventType::GuildUpdate, "GUILD_UPDATE"), + (EventType::IntegrationCreate, "INTEGRATION_CREATE"), + (EventType::IntegrationDelete, "INTEGRATION_DELETE"), + (EventType::IntegrationUpdate, "INTEGRATION_UPDATE"), + (EventType::InteractionCreate, "INTERACTION_CREATE"), + (EventType::InviteCreate, "INVITE_CREATE"), + (EventType::InviteDelete, "INVITE_DELETE"), + (EventType::MemberAdd, "GUILD_MEMBER_ADD"), + (EventType::MemberChunk, "GUILD_MEMBERS_CHUNK"), + (EventType::MemberRemove, "GUILD_MEMBER_REMOVE"), + (EventType::MemberUpdate, "GUILD_MEMBER_UPDATE"), + (EventType::MessageCreate, "MESSAGE_CREATE"), + (EventType::MessageDelete, "MESSAGE_DELETE"), + (EventType::MessageDeleteBulk, "MESSAGE_DELETE_BULK"), + (EventType::MessageUpdate, "MESSAGE_UPDATE"), + (EventType::PresenceUpdate, "PRESENCE_UPDATE"), + (EventType::PresencesReplace, "PRESENCES_REPLACE"), + (EventType::ReactionAdd, "MESSAGE_REACTION_ADD"), + (EventType::ReactionRemove, "MESSAGE_REACTION_REMOVE"), + (EventType::ReactionRemoveAll, "MESSAGE_REACTION_REMOVE_ALL"), + ( EventType::ReactionRemoveEmoji, "MESSAGE_REACTION_REMOVE_EMOJI", - ); - assert_variant(EventType::Ready, "READY"); - assert_variant(EventType::Resumed, "RESUMED"); - assert_variant(EventType::RoleCreate, "GUILD_ROLE_CREATE"); - assert_variant(EventType::RoleDelete, "GUILD_ROLE_DELETE"); - assert_variant(EventType::RoleUpdate, "GUILD_ROLE_UPDATE"); - assert_variant(EventType::StageInstanceCreate, "STAGE_INSTANCE_CREATE"); - assert_variant(EventType::StageInstanceDelete, "STAGE_INSTANCE_DELETE"); - assert_variant(EventType::StageInstanceUpdate, "STAGE_INSTANCE_UPDATE"); - assert_variant(EventType::ThreadCreate, "THREAD_CREATE"); - assert_variant(EventType::ThreadDelete, "THREAD_DELETE"); - assert_variant(EventType::ThreadListSync, "THREAD_LIST_SYNC"); - assert_variant(EventType::ThreadMemberUpdate, "THREAD_MEMBER_UPDATE"); - assert_variant(EventType::ThreadMembersUpdate, "THREAD_MEMBERS_UPDATE"); - assert_variant(EventType::ThreadUpdate, "THREAD_UPDATE"); - assert_variant(EventType::TypingStart, "TYPING_START"); - assert_variant(EventType::UnavailableGuild, "UNAVAILABLE_GUILD"); - assert_variant(EventType::UserUpdate, "USER_UPDATE"); - assert_variant(EventType::VoiceServerUpdate, "VOICE_SERVER_UPDATE"); - assert_variant(EventType::VoiceStateUpdate, "VOICE_STATE_UPDATE"); - assert_variant(EventType::WebhooksUpdate, "WEBHOOKS_UPDATE"); + ), + (EventType::Ready, "READY"), + (EventType::Resumed, "RESUMED"), + (EventType::RoleCreate, "GUILD_ROLE_CREATE"), + (EventType::RoleDelete, "GUILD_ROLE_DELETE"), + (EventType::RoleUpdate, "GUILD_ROLE_UPDATE"), + (EventType::StageInstanceCreate, "STAGE_INSTANCE_CREATE"), + (EventType::StageInstanceDelete, "STAGE_INSTANCE_DELETE"), + (EventType::StageInstanceUpdate, "STAGE_INSTANCE_UPDATE"), + (EventType::ThreadCreate, "THREAD_CREATE"), + (EventType::ThreadDelete, "THREAD_DELETE"), + (EventType::ThreadListSync, "THREAD_LIST_SYNC"), + (EventType::ThreadMemberUpdate, "THREAD_MEMBER_UPDATE"), + (EventType::ThreadMembersUpdate, "THREAD_MEMBERS_UPDATE"), + (EventType::ThreadUpdate, "THREAD_UPDATE"), + (EventType::TypingStart, "TYPING_START"), + (EventType::UnavailableGuild, "UNAVAILABLE_GUILD"), + (EventType::UserUpdate, "USER_UPDATE"), + (EventType::VoiceServerUpdate, "VOICE_SERVER_UPDATE"), + (EventType::VoiceStateUpdate, "VOICE_STATE_UPDATE"), + (EventType::WebhooksUpdate, "WEBHOOKS_UPDATE"), + ]; + + #[test] + fn serde() { + for (value, name) in MAP { + serde_test::assert_tokens( + value, + &[Token::UnitVariant { + name: "EventType", + variant: name, + }], + ); + assert_eq!(value.name().unwrap(), *name); + } } } diff --git a/twilight-model/src/gateway/event/mod.rs b/twilight-model/src/gateway/event/mod.rs index 6ca49841978..4f1b95a814f 100644 --- a/twilight-model/src/gateway/event/mod.rs +++ b/twilight-model/src/gateway/event/mod.rs @@ -1,12 +1,26 @@ +//! Receivable shard payloads. +//! +//! The [`Event`] enum is a convenient flattened [`GatewayEvent`]. +//! +//! # Deserializing +//! +//! [`GatewayEvent`] does not implement [`Deserialize`] since that would require +//! the `op` ([`OpCode`]) and `t` ([`DispatchEventType`]) fields to be +//! deserialized before the `d` ([`DispatchEvent`] and friends) field. +//! Instead you should use [`GatewayEventDeserializer`], which implements +//! [`DeserializeSeed`] into [`GatewayEvent`]. +//! +//! [`Deserialize`]: serde::Deserialize +//! [`DeserializeSeed`]: serde::de::DeserializeSeed +//! [`OpCode`]: super::OpCode #![allow(clippy::wildcard_imports)] -pub mod gateway; - mod dispatch; +mod gateway; mod kind; pub use self::{ - dispatch::{DispatchEvent, DispatchEventWithTypeDeserializer}, + dispatch::{DispatchEvent, DispatchEventType}, gateway::{GatewayEvent, GatewayEventDeserializer}, kind::EventType, }; @@ -16,10 +30,10 @@ use crate::id::{marker::GuildMarker, Id}; use std::error::Error; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; -/// Any type of event that a shard emits. +/// Receivable gateway event. /// -/// This brings together all of the types of [`DispatchEvent`] and -/// [`GatewayEvent`]. +/// Flattens all variants of [`DispatchEvent`] and [`GatewayEvent`] into a +/// single type (but drops dispatch events' sequence number). #[derive(Clone, Debug, PartialEq)] pub enum Event { /// Message was blocked by AutoMod according to a rule. @@ -170,87 +184,86 @@ pub enum Event { } impl Event { - /// Retrieve the guild ID of an event if it took place in a guild. - /// - /// While events such as [`MessageDelete`] will never include a guild ID, events - /// such as [`BanAdd`] and only some [`Channel`] related events will include - /// one. Guild variants will include a guild ID while DM Channels don't. + /// ID of the guild from which the event originated, if known. /// - /// [`Channel`]: crate::channel::Channel + /// Some events, such as [`MessageDelete`], will never include a guild ID, + /// others, such as [`BanAdd`], will always include one and some events, + /// such as [`ChannelCreate`] might include one. pub const fn guild_id(&self) -> Option> { match self { - Event::AutoModerationActionExecution(e) => Some(e.guild_id), - Event::AutoModerationRuleCreate(e) => Some(e.0.guild_id), - Event::AutoModerationRuleDelete(e) => Some(e.0.guild_id), - Event::AutoModerationRuleUpdate(e) => Some(e.0.guild_id), - Event::BanAdd(e) => Some(e.guild_id), - Event::BanRemove(e) => Some(e.guild_id), - Event::ChannelCreate(e) => e.0.guild_id, - Event::ChannelDelete(e) => e.0.guild_id, - Event::ChannelUpdate(e) => e.0.guild_id, - Event::CommandPermissionsUpdate(e) => Some(e.0.guild_id), - Event::GuildCreate(e) => Some(e.0.id), - Event::GuildDelete(e) => Some(e.id), - Event::GuildEmojisUpdate(e) => Some(e.guild_id), - Event::GuildIntegrationsUpdate(e) => Some(e.guild_id), - Event::GuildScheduledEventCreate(e) => Some(e.0.guild_id), - Event::GuildScheduledEventDelete(e) => Some(e.0.guild_id), - Event::GuildScheduledEventUpdate(e) => Some(e.0.guild_id), - Event::GuildScheduledEventUserAdd(e) => Some(e.guild_id), - Event::GuildScheduledEventUserRemove(e) => Some(e.guild_id), - Event::GuildStickersUpdate(e) => Some(e.guild_id), - Event::GuildUpdate(e) => Some(e.0.id), - Event::IntegrationCreate(e) => e.0.guild_id, - Event::IntegrationDelete(e) => Some(e.guild_id), - Event::IntegrationUpdate(e) => e.0.guild_id, - Event::InteractionCreate(e) => e.0.guild_id, - Event::InviteCreate(e) => Some(e.guild_id), - Event::InviteDelete(e) => Some(e.guild_id), - Event::MemberAdd(e) => Some(e.0.guild_id), - Event::MemberChunk(e) => Some(e.guild_id), - Event::MemberRemove(e) => Some(e.guild_id), - Event::MemberUpdate(e) => Some(e.guild_id), - Event::MessageCreate(e) => e.0.guild_id, - Event::PresenceUpdate(e) => Some(e.0.guild_id), - Event::ReactionAdd(e) => e.0.guild_id, - Event::ReactionRemove(e) => e.0.guild_id, - Event::ReactionRemoveAll(e) => e.guild_id, - Event::ReactionRemoveEmoji(e) => Some(e.guild_id), - Event::RoleCreate(e) => Some(e.guild_id), - Event::RoleDelete(e) => Some(e.guild_id), - Event::RoleUpdate(e) => Some(e.guild_id), - Event::StageInstanceCreate(e) => Some(e.0.guild_id), - Event::StageInstanceDelete(e) => Some(e.0.guild_id), - Event::StageInstanceUpdate(e) => Some(e.0.guild_id), - Event::ThreadCreate(e) => e.0.guild_id, - Event::ThreadDelete(e) => Some(e.guild_id), - Event::ThreadListSync(e) => Some(e.guild_id), - Event::ThreadMembersUpdate(e) => Some(e.guild_id), - Event::ThreadUpdate(e) => e.0.guild_id, - Event::TypingStart(e) => e.guild_id, - Event::UnavailableGuild(e) => Some(e.id), - Event::VoiceServerUpdate(e) => Some(e.guild_id), - Event::VoiceStateUpdate(e) => e.0.guild_id, - Event::WebhooksUpdate(e) => Some(e.guild_id), - Event::ChannelPinsUpdate(_) - | Event::GatewayClose(_) - | Event::GatewayHeartbeat(_) - | Event::GatewayHeartbeatAck - | Event::GatewayHello(_) - | Event::GatewayInvalidateSession(_) - | Event::GatewayReconnect - | Event::GiftCodeUpdate - | Event::MessageDelete(_) - | Event::MessageDeleteBulk(_) - | Event::MessageUpdate(_) - | Event::PresencesReplace - | Event::Ready(_) - | Event::Resumed - | Event::ThreadMemberUpdate(_) - | Event::UserUpdate(_) => None, + Self::AutoModerationActionExecution(e) => Some(e.guild_id), + Self::AutoModerationRuleCreate(e) => Some(e.0.guild_id), + Self::AutoModerationRuleDelete(e) => Some(e.0.guild_id), + Self::AutoModerationRuleUpdate(e) => Some(e.0.guild_id), + Self::BanAdd(e) => Some(e.guild_id), + Self::BanRemove(e) => Some(e.guild_id), + Self::ChannelCreate(e) => e.0.guild_id, + Self::ChannelDelete(e) => e.0.guild_id, + Self::ChannelUpdate(e) => e.0.guild_id, + Self::CommandPermissionsUpdate(e) => Some(e.0.guild_id), + Self::GuildCreate(e) => Some(e.0.id), + Self::GuildDelete(e) => Some(e.id), + Self::GuildEmojisUpdate(e) => Some(e.guild_id), + Self::GuildIntegrationsUpdate(e) => Some(e.guild_id), + Self::GuildScheduledEventCreate(e) => Some(e.0.guild_id), + Self::GuildScheduledEventDelete(e) => Some(e.0.guild_id), + Self::GuildScheduledEventUpdate(e) => Some(e.0.guild_id), + Self::GuildScheduledEventUserAdd(e) => Some(e.guild_id), + Self::GuildScheduledEventUserRemove(e) => Some(e.guild_id), + Self::GuildStickersUpdate(e) => Some(e.guild_id), + Self::GuildUpdate(e) => Some(e.0.id), + Self::IntegrationCreate(e) => e.0.guild_id, + Self::IntegrationDelete(e) => Some(e.guild_id), + Self::IntegrationUpdate(e) => e.0.guild_id, + Self::InteractionCreate(e) => e.0.guild_id, + Self::InviteCreate(e) => Some(e.guild_id), + Self::InviteDelete(e) => Some(e.guild_id), + Self::MemberAdd(e) => Some(e.0.guild_id), + Self::MemberChunk(e) => Some(e.guild_id), + Self::MemberRemove(e) => Some(e.guild_id), + Self::MemberUpdate(e) => Some(e.guild_id), + Self::MessageCreate(e) => e.0.guild_id, + Self::PresenceUpdate(e) => Some(e.0.guild_id), + Self::ReactionAdd(e) => e.0.guild_id, + Self::ReactionRemove(e) => e.0.guild_id, + Self::ReactionRemoveAll(e) => e.guild_id, + Self::ReactionRemoveEmoji(e) => Some(e.guild_id), + Self::RoleCreate(e) => Some(e.guild_id), + Self::RoleDelete(e) => Some(e.guild_id), + Self::RoleUpdate(e) => Some(e.guild_id), + Self::StageInstanceCreate(e) => Some(e.0.guild_id), + Self::StageInstanceDelete(e) => Some(e.0.guild_id), + Self::StageInstanceUpdate(e) => Some(e.0.guild_id), + Self::ThreadCreate(e) => e.0.guild_id, + Self::ThreadDelete(e) => Some(e.guild_id), + Self::ThreadListSync(e) => Some(e.guild_id), + Self::ThreadMembersUpdate(e) => Some(e.guild_id), + Self::ThreadUpdate(e) => e.0.guild_id, + Self::TypingStart(e) => e.guild_id, + Self::UnavailableGuild(e) => Some(e.id), + Self::VoiceServerUpdate(e) => Some(e.guild_id), + Self::VoiceStateUpdate(e) => e.0.guild_id, + Self::WebhooksUpdate(e) => Some(e.guild_id), + Self::ChannelPinsUpdate(_) + | Self::GatewayClose(_) + | Self::GatewayHeartbeat(_) + | Self::GatewayHeartbeatAck + | Self::GatewayHello(_) + | Self::GatewayInvalidateSession(_) + | Self::GatewayReconnect + | Self::GiftCodeUpdate + | Self::MessageDelete(_) + | Self::MessageDeleteBulk(_) + | Self::MessageUpdate(_) + | Self::PresencesReplace + | Self::Ready(_) + | Self::Resumed + | Self::ThreadMemberUpdate(_) + | Self::UserUpdate(_) => None, } } + /// Type of event. pub const fn kind(&self) -> EventType { match self { Self::AutoModerationActionExecution(_) => EventType::AutoModerationActionExecution, @@ -443,93 +456,3 @@ impl Display for EventConversionError { } impl Error for EventConversionError {} - -#[cfg(test)] -mod tests { - //! `EVENT_THRESHOLD` is equivalent to 192 bytes. This was decided based on - //! the size of `Event` at the time of writing. The assertions here are to - //! ensure that in the case the events themselves grow or shrink past the - //! threshold, they are properly boxed or unboxed respectively. - //! - //! If a field has been added to an event in the "unboxed" section and its - //! assertion now fails, then you will need to wrap the event in a box in - //! the `Event` type and move the assertion to the "boxed" section. - //! - //! Likewise, if a field has been removed from an event in the "boxed" - //! section and the assertion now fails, you will need to remove the box - //! wrapping the event in the `Event` type and move the assertion to the - //! "unboxed" section. - - use super::{super::payload::incoming::*, Event}; - use static_assertions::const_assert; - use std::mem; - - // `dead_code`: `const_assert` operates at the compiler level, and the lint - // requires a variable to be used in a function, so this is a false - // positive. - #[allow(dead_code)] - const EVENT_THRESHOLD: usize = 184; - - const_assert!(mem::size_of::() == EVENT_THRESHOLD); - - // Boxed events. - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - const_assert!(mem::size_of::() > EVENT_THRESHOLD); - - // Unboxed. - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); - const_assert!(mem::size_of::() <= EVENT_THRESHOLD); -}