diff options
| author | Zeyla Hellyer <[email protected]> | 2017-11-19 09:53:59 -0800 |
|---|---|---|
| committer | Zeyla Hellyer <[email protected]> | 2017-11-19 09:53:59 -0800 |
| commit | c3aa63faee8b3ae6d5126aa27a74876766c61e4c (patch) | |
| tree | 560ae5fcf011a8bb7cbaa5e4a5fc3bb2ee89ee31 /src | |
| parent | Add `model::Reaction::user` (diff) | |
| download | serenity-c3aa63faee8b3ae6d5126aa27a74876766c61e4c.tar.xz serenity-c3aa63faee8b3ae6d5126aa27a74876766c61e4c.zip | |
Implement Deserialize for {,Gateway,Voice}Event
Implement Deserialize for `model::event::GatewayEvent` and
`model::event::VoiceEvent`, and derive it for `model::event::Event`.
Due to the natural potential slowness of deserializing into`Event`
(attempting to deserialize into each variant until successful), a
function named `model::event::deserialize_event_with_type` is provided
for quickly deserializing into a known type if the dispatch type is
known.
Diffstat (limited to 'src')
| -rw-r--r-- | src/client/bridge/gateway/shard_runner.rs | 9 | ||||
| -rw-r--r-- | src/internal/ws_impl.rs | 24 | ||||
| -rw-r--r-- | src/model/event.rs | 629 | ||||
| -rw-r--r-- | src/voice/connection.rs | 35 |
4 files changed, 518 insertions, 179 deletions
diff --git a/src/client/bridge/gateway/shard_runner.rs b/src/client/bridge/gateway/shard_runner.rs index 005f321..15712f3 100644 --- a/src/client/bridge/gateway/shard_runner.rs +++ b/src/client/bridge/gateway/shard_runner.rs @@ -3,6 +3,7 @@ use internal::prelude::*; use internal::ws_impl::ReceiverExt; use model::event::{Event, GatewayEvent}; use parking_lot::Mutex; +use serde::Deserialize; use std::sync::mpsc::{self, Receiver, Sender, TryRecvError}; use std::sync::Arc; use super::super::super::{EventHandler, dispatch}; @@ -329,7 +330,11 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { /// Returns a received event, as well as whether reading the potentially /// present event was successful. fn recv_event(&mut self) -> (Option<Event>, Option<ShardAction>, bool) { - let gw_event = match self.shard.client.recv_json(GatewayEvent::decode) { + let gw_event = match self.shard.client.recv_json() { + Ok(Some(value)) => { + GatewayEvent::deserialize(value).map(Some).map_err(From::from) + }, + Ok(None) => Ok(None), Err(Error::WebSocket(WebSocketError::IoError(_))) => { // Check that an amount of time at least double the // heartbeat_interval has passed. @@ -366,7 +371,7 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { // hit every iteration. return (None, None, false); }, - other => other, + Err(why) => Err(why), }; let event = match gw_event { diff --git a/src/internal/ws_impl.rs b/src/internal/ws_impl.rs index c1ac83b..8988e06 100644 --- a/src/internal/ws_impl.rs +++ b/src/internal/ws_impl.rs @@ -7,8 +7,7 @@ use websocket::sync::stream::{TcpStream, TlsStream}; use websocket::sync::Client as WsClient; pub trait ReceiverExt { - fn recv_json<F, T>(&mut self, decode: F) -> Result<Option<T>> - where F: Fn(Value) -> Result<T>; + fn recv_json(&mut self) -> Result<Option<Value>>; } pub trait SenderExt { @@ -16,29 +15,14 @@ pub trait SenderExt { } impl ReceiverExt for WsClient<TlsStream<TcpStream>> { - fn recv_json<F, T>(&mut self, decode: F) -> Result<Option<T>> - where F: Fn(Value) -> Result<T> { + fn recv_json(&mut self) -> Result<Option<Value>> { Ok(match self.recv_message()? { OwnedMessage::Binary(bytes) => { - let value = serde_json::from_reader(ZlibDecoder::new(&bytes[..]))?; - - Some(decode(value).map_err(|why| { - let s = String::from_utf8_lossy(&bytes); - - warn!("(╯°□°)╯︵ ┻━┻ Error decoding: {}", s); - - why - })?) + serde_json::from_reader(ZlibDecoder::new(&bytes[..])).map(Some)? }, OwnedMessage::Close(data) => return Err(Error::Gateway(GatewayError::Closed(data))), OwnedMessage::Text(payload) => { - let value = serde_json::from_str(&payload)?; - - Some(decode(value).map_err(|why| { - warn!("(╯°□°)╯︵ ┻━┻ Error decoding: {}", payload); - - why - })?) + serde_json::from_str(&payload).map(Some)? }, OwnedMessage::Ping(x) => { self.send_message(&OwnedMessage::Pong(x)) diff --git a/src/model/event.rs b/src/model/event.rs index 4e71101..b690f96 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -3,8 +3,6 @@ use chrono::{DateTime, FixedOffset}; use serde::de::Error as DeError; use serde_json; -#[cfg(feature = "voice")] -use serde_json::Error as JsonError; use std::collections::HashMap; use super::utils::deserialize_emojis; use super::*; @@ -15,8 +13,6 @@ use internal::prelude::*; use cache::{Cache, CacheUpdate}; #[cfg(feature = "gateway")] use constants::OpCode; -#[cfg(feature = "gateway")] -use gateway::GatewayError; #[cfg(feature = "cache")] use internal::RwLockExt; #[cfg(feature = "cache")] @@ -953,7 +949,7 @@ pub struct TypingStartEvent { pub user_id: UserId, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize)] pub struct UnknownEvent { pub kind: String, pub value: Value, @@ -989,7 +985,6 @@ pub struct VoiceServerUpdateEvent { pub token: String, } - #[derive(Clone, Debug)] pub struct VoiceStateUpdateEvent { pub guild_id: Option<GuildId>, @@ -1067,33 +1062,39 @@ pub enum GatewayEvent { HeartbeatAck, } -impl GatewayEvent { - #[cfg(feature = "gateway")] - pub fn decode(value: Value) -> Result<Self> { - let mut map = JsonMap::deserialize(value)?; +impl<'de> Deserialize<'de> for GatewayEvent { + fn deserialize<D: Deserializer<'de>>(deserializer: D) + -> StdResult<Self, D::Error> { + let mut map = JsonMap::deserialize(deserializer)?; let op = map.remove("op") - .ok_or_else(|| DeError::custom("expected gateway event op")) - .and_then(OpCode::deserialize)?; + .ok_or_else(|| DeError::custom("expected op")) + .and_then(OpCode::deserialize) + .map_err(DeError::custom)?; Ok(match op { OpCode::Event => { let s = map.remove("s") .ok_or_else(|| DeError::custom("expected gateway event sequence")) - .and_then(u64::deserialize)?; - let t = map.remove("t") + .and_then(u64::deserialize) + .map_err(DeError::custom)?; + let kind = map.remove("t") .ok_or_else(|| DeError::custom("expected gateway event type")) - .and_then(String::deserialize)?; - let d = map.remove("d").ok_or_else(|| { + .and_then(EventType::deserialize) + .map_err(DeError::custom)?; + let payload = map.remove("d").ok_or_else(|| { Error::Decode("expected gateway event d", Value::Object(map)) - })?; + }).map_err(DeError::custom)?; + + let x = deserialize_event_with_type(kind, payload).unwrap(); - GatewayEvent::Dispatch(s, Event::decode(t, d)?) + GatewayEvent::Dispatch(s, x) }, OpCode::Heartbeat => { let s = map.remove("s") .ok_or_else(|| DeError::custom("Expected heartbeat s")) - .and_then(u64::deserialize)?; + .and_then(u64::deserialize) + .map_err(DeError::custom)?; GatewayEvent::Heartbeat(s) }, @@ -1103,29 +1104,32 @@ impl GatewayEvent { .ok_or_else(|| { DeError::custom("expected gateway invalid session d") }) - .and_then(bool::deserialize)?; + .and_then(bool::deserialize) + .map_err(DeError::custom)?; GatewayEvent::InvalidateSession(resumable) }, OpCode::Hello => { let mut d = map.remove("d") .ok_or_else(|| DeError::custom("expected gateway hello d")) - .and_then(JsonMap::deserialize)?; + .and_then(JsonMap::deserialize) + .map_err(DeError::custom)?; let interval = d.remove("heartbeat_interval") .ok_or_else(|| DeError::custom("expected gateway hello interval")) - .and_then(u64::deserialize)?; + .and_then(u64::deserialize) + .map_err(DeError::custom)?; GatewayEvent::Hello(interval) }, OpCode::HeartbeatAck => GatewayEvent::HeartbeatAck, - _ => return Err(Error::Gateway(GatewayError::InvalidOpCode)), + _ => return Err(DeError::custom("invalid opcode")), }) } } /// Event received over a websocket connection #[allow(large_enum_variant)] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize)] pub enum Event { /// A [`Channel`] was created. /// @@ -1239,106 +1243,428 @@ pub enum Event { Unknown(UnknownEvent), } -impl Event { - #[allow(cyclomatic_complexity)] - #[cfg(feature = "gateway")] - fn decode(kind: String, value: Value) -> Result<Event> { - Ok(match &kind[..] { - "CHANNEL_CREATE" => Event::ChannelCreate(ChannelCreateEvent::deserialize(value)?), - "CHANNEL_DELETE" => Event::ChannelDelete(ChannelDeleteEvent::deserialize(value)?), - "CHANNEL_PINS_UPDATE" => { - Event::ChannelPinsUpdate(ChannelPinsUpdateEvent::deserialize(value)?) - }, - "CHANNEL_RECIPIENT_ADD" => { - Event::ChannelRecipientAdd(ChannelRecipientAddEvent::deserialize(value)?) - }, - "CHANNEL_RECIPIENT_REMOVE" => { - Event::ChannelRecipientRemove(ChannelRecipientRemoveEvent::deserialize(value)?) - }, - "CHANNEL_UPDATE" => Event::ChannelUpdate(ChannelUpdateEvent::deserialize(value)?), - "GUILD_BAN_ADD" => Event::GuildBanAdd(GuildBanAddEvent::deserialize(value)?), - "GUILD_BAN_REMOVE" => Event::GuildBanRemove(GuildBanRemoveEvent::deserialize(value)?), - "GUILD_CREATE" => { - let mut map = JsonMap::deserialize(value)?; - - if map.remove("unavailable") - .and_then(|v| v.as_bool()) - .unwrap_or(false) { - Event::GuildUnavailable(GuildUnavailableEvent::deserialize(Value::Object(map))?) - } else { - Event::GuildCreate(GuildCreateEvent::deserialize(Value::Object(map))?) - } - }, - "GUILD_DELETE" => { - let mut map = JsonMap::deserialize(value)?; +/// Deserializes a `serde_json::Value` into an `Event`. +/// +/// The given `EventType` is used to determine what event to deserialize into. +/// For example, an [`EventType::ChannelCreate`] will cause the given value to +/// attempt to be deserialized into a [`ChannelCreateEvent`]. +/// +/// Special handling is done in regards to [`EventType::GuildCreate`] and +/// [`EventType::GuildDelete`]: they check for an `"unavailable"` key and, if +/// present and containing a value of `true`, will cause a +/// [`GuildUnavailableEvent`] to be returned. Otherwise, all other event types +/// correlate to the deserialization of their appropriate event. +/// +/// [`EventType::ChannelCreate`]: enum.EventType.html#variant.ChannelCreate +/// [`EventType::GuildCreate`]: enum.EventType.html#variant.GuildCreate +/// [`EventType::GuildDelete`]: enum.EventType.html#variant.GuildDelete +/// [`ChannelCreateEvent`]: struct.ChannelCreateEvent.html +/// [`GuildUnavailableEvent`]: struct.GuildUnavailableEvent.html +pub fn deserialize_event_with_type(kind: EventType, v: Value) -> Result<Event> { + Ok(match kind { + EventType::ChannelCreate => Event::ChannelCreate(serde_json::from_value(v)?), + EventType::ChannelDelete => Event::ChannelDelete(serde_json::from_value(v)?), + EventType::ChannelPinsUpdate => { + Event::ChannelPinsUpdate(serde_json::from_value(v)?) + }, + EventType::ChannelRecipientAdd => { + Event::ChannelRecipientAdd(serde_json::from_value(v)?) + }, + EventType::ChannelRecipientRemove => { + Event::ChannelRecipientRemove(serde_json::from_value(v)?) + }, + EventType::ChannelUpdate => Event::ChannelUpdate(serde_json::from_value(v)?), + EventType::GuildBanAdd => Event::GuildBanAdd(serde_json::from_value(v)?), + EventType::GuildBanRemove => Event::GuildBanRemove(serde_json::from_value(v)?), + EventType::GuildCreate | EventType::GuildUnavailable => { + // GuildUnavailable isn't actually received from the gateway, so it + // can be lumped in with GuildCreate's arm. + + let mut map = JsonMap::deserialize(v)?; + + if map.remove("unavailable") + .and_then(|v| v.as_bool()) + .unwrap_or(false) { + let guild_data = serde_json::from_value(Value::Object(map))?; + + Event::GuildUnavailable(guild_data) + } else { + Event::GuildCreate(serde_json::from_value(Value::Object(map))?) + } + }, + EventType::GuildDelete => { + let mut map = JsonMap::deserialize(v)?; - if map.remove("unavailable") - .and_then(|v| v.as_bool()) - .unwrap_or(false) { - Event::GuildUnavailable(GuildUnavailableEvent::deserialize(Value::Object(map))?) - } else { - Event::GuildDelete(GuildDeleteEvent::deserialize(Value::Object(map))?) - } - }, - "GUILD_EMOJIS_UPDATE" => { - Event::GuildEmojisUpdate(GuildEmojisUpdateEvent::deserialize(value)?) - }, - "GUILD_INTEGRATIONS_UPDATE" => { - Event::GuildIntegrationsUpdate(GuildIntegrationsUpdateEvent::deserialize(value)?) - }, - "GUILD_MEMBER_ADD" => Event::GuildMemberAdd(GuildMemberAddEvent::deserialize(value)?), - "GUILD_MEMBER_REMOVE" => { - Event::GuildMemberRemove(GuildMemberRemoveEvent::deserialize(value)?) - }, - "GUILD_MEMBER_UPDATE" => { - Event::GuildMemberUpdate(GuildMemberUpdateEvent::deserialize(value)?) - }, - "GUILD_MEMBERS_CHUNK" => { - Event::GuildMembersChunk(GuildMembersChunkEvent::deserialize(value)?) - }, - "GUILD_ROLE_CREATE" => { - Event::GuildRoleCreate(GuildRoleCreateEvent::deserialize(value)?) - }, - "GUILD_ROLE_DELETE" => { - Event::GuildRoleDelete(GuildRoleDeleteEvent::deserialize(value)?) - }, - "GUILD_ROLE_UPDATE" => { - Event::GuildRoleUpdate(GuildRoleUpdateEvent::deserialize(value)?) - }, - "GUILD_UPDATE" => Event::GuildUpdate(GuildUpdateEvent::deserialize(value)?), - "MESSAGE_CREATE" => Event::MessageCreate(MessageCreateEvent::deserialize(value)?), - "MESSAGE_DELETE" => Event::MessageDelete(MessageDeleteEvent::deserialize(value)?), - "MESSAGE_DELETE_BULK" => { - Event::MessageDeleteBulk(MessageDeleteBulkEvent::deserialize(value)?) - }, - "MESSAGE_REACTION_ADD" => Event::ReactionAdd(ReactionAddEvent::deserialize(value)?), - "MESSAGE_REACTION_REMOVE" => { - Event::ReactionRemove(ReactionRemoveEvent::deserialize(value)?) - }, - "MESSAGE_REACTION_REMOVE_ALL" => { - Event::ReactionRemoveAll(ReactionRemoveAllEvent::deserialize(value)?) - }, - "MESSAGE_UPDATE" => Event::MessageUpdate(MessageUpdateEvent::deserialize(value)?), - "PRESENCE_UPDATE" => Event::PresenceUpdate(PresenceUpdateEvent::deserialize(value)?), - "PRESENCES_REPLACE" => { - Event::PresencesReplace(PresencesReplaceEvent::deserialize(value)?) - }, - "READY" => Event::Ready(ReadyEvent::deserialize(value)?), - "RESUMED" => Event::Resumed(ResumedEvent::deserialize(value)?), - "TYPING_START" => Event::TypingStart(TypingStartEvent::deserialize(value)?), - "USER_UPDATE" => Event::UserUpdate(UserUpdateEvent::deserialize(value)?), - "VOICE_SERVER_UPDATE" => { - Event::VoiceServerUpdate(VoiceServerUpdateEvent::deserialize(value)?) - }, - "VOICE_STATE_UPDATE" => { - Event::VoiceStateUpdate(VoiceStateUpdateEvent::deserialize(value)?) - }, - "WEBHOOKS_UPDATE" => Event::WebhookUpdate(WebhookUpdateEvent::deserialize(value)?), - _ => Event::Unknown(UnknownEvent { - kind: kind, - value: value, - }), - }) + if map.remove("unavailable") + .and_then(|v| v.as_bool()) + .unwrap_or(false) { + let guild_data = serde_json::from_value(Value::Object(map))?; + + Event::GuildUnavailable(guild_data) + } else { + Event::GuildDelete(serde_json::from_value(Value::Object(map))?) + } + }, + EventType::GuildEmojisUpdate => { + Event::GuildEmojisUpdate(serde_json::from_value(v)?) + }, + EventType::GuildIntegrationsUpdate => { + Event::GuildIntegrationsUpdate(serde_json::from_value(v)?) + }, + EventType::GuildMemberAdd => Event::GuildMemberAdd(serde_json::from_value(v)?), + EventType::GuildMemberRemove => { + Event::GuildMemberRemove(serde_json::from_value(v)?) + }, + EventType::GuildMemberUpdate => { + Event::GuildMemberUpdate(serde_json::from_value(v)?) + }, + EventType::GuildMembersChunk => { + Event::GuildMembersChunk(serde_json::from_value(v)?) + }, + EventType::GuildRoleCreate => { + Event::GuildRoleCreate(serde_json::from_value(v)?) + }, + EventType::GuildRoleDelete => { + Event::GuildRoleDelete(serde_json::from_value(v)?) + }, + EventType::GuildRoleUpdate => { + Event::GuildRoleUpdate(serde_json::from_value(v)?) + }, + EventType::GuildUpdate => Event::GuildUpdate(serde_json::from_value(v)?), + EventType::MessageCreate => Event::MessageCreate(serde_json::from_value(v)?), + EventType::MessageDelete => Event::MessageDelete(serde_json::from_value(v)?), + EventType::MessageDeleteBulk => { + Event::MessageDeleteBulk(serde_json::from_value(v)?) + }, + EventType::ReactionAdd => { + Event::ReactionAdd(serde_json::from_value(v)?) + }, + EventType::ReactionRemove => { + Event::ReactionRemove(serde_json::from_value(v)?) + }, + EventType::ReactionRemoveAll => { + Event::ReactionRemoveAll(serde_json::from_value(v)?) + }, + EventType::MessageUpdate => Event::MessageUpdate(serde_json::from_value(v)?), + EventType::PresenceUpdate => Event::PresenceUpdate(serde_json::from_value(v)?), + EventType::PresencesReplace => { + Event::PresencesReplace(serde_json::from_value(v)?) + }, + EventType::Ready => Event::Ready(serde_json::from_value(v)?), + EventType::Resumed => Event::Resumed(serde_json::from_value(v)?), + EventType::TypingStart => Event::TypingStart(serde_json::from_value(v)?), + EventType::UserUpdate => Event::UserUpdate(serde_json::from_value(v)?), + EventType::VoiceServerUpdate => { + Event::VoiceServerUpdate(serde_json::from_value(v)?) + }, + EventType::VoiceStateUpdate => { + Event::VoiceStateUpdate(serde_json::from_value(v)?) + }, + EventType::WebhookUpdate => Event::WebhookUpdate(serde_json::from_value(v)?), + EventType::Other(kind) => Event::Unknown(UnknownEvent { + kind: kind.to_owned(), + value: v, + }), + }) +} + +/// The type of event dispatch received from the gateway. +/// +/// This is useful for deciding how to deserialize a received payload. +/// +/// A Deserialization implementation is provided for deserializing raw event +/// dispatch type strings to this enum, e.g. deserializing `"CHANNEL_CREATE"` to +/// [`EventType::ChannelCreate`]. +/// +/// [`EventType::ChannelCreate`]: enum.EventType.html#variant.ChannelCreate +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub enum EventType { + /// Indicator that a channel create payload was received. + /// + /// This maps to [`ChannelCreateEvent`]. + /// + /// [`ChannelCreateEvent`]: struct.ChannelCreateEvent.html + ChannelCreate, + /// Indicator that a channel delete payload was received. + /// + /// This maps to [`ChannelDeleteEvent`]. + /// + /// [`ChannelDeleteEvent`]: struct.ChannelDeleteEvent.html + ChannelDelete, + /// Indicator that a channel pins update payload was received. + /// + /// This maps to [`ChannelPinsUpdateEvent`]. + /// + /// [`ChannelPinsUpdateEvent`]: struct.ChannelPinsUpdateEvent.html + ChannelPinsUpdate, + /// Indicator that a channel recipient addition payload was received. + /// + /// This maps to [`ChannelRecipientAddEvent`]. + /// + /// [`ChannelRecipientAddEvent`]: struct.ChannelRecipientAddEvent.html + ChannelRecipientAdd, + /// Indicator that a channel recipient removal payload was received. + /// + /// This maps to [`ChannelRecipientRemoveEvent`]. + /// + /// [`ChannelRecipientRemoveEvent`]: struct.ChannelRecipientRemoveEvent.html + ChannelRecipientRemove, + /// Indicator that a channel update payload was received. + /// + /// This maps to [`ChannelUpdateEvent`]. + /// + /// [`ChannelUpdateEvent`]: struct.ChannelUpdateEvent.html + ChannelUpdate, + /// Indicator that a guild ban addition payload was received. + /// + /// This maps to [`GuildBanAddEvent`]. + /// + /// [`GuildBanAddEvent`]: struct.GuildBanAddEvent.html + GuildBanAdd, + /// Indicator that a guild ban removal payload was received. + /// + /// This maps to [`GuildBanRemoveEvent`]. + /// + /// [`GuildBanRemoveEvent`]: struct.GuildBanRemoveEvent.html + GuildBanRemove, + /// Indicator that a guild create payload was received. + /// + /// This maps to [`GuildCreateEvent`]. + /// + /// [`GuildCreateEvent`]: struct.GuildCreateEvent.html + GuildCreate, + /// Indicator that a guild delete payload was received. + /// + /// This maps to [`GuildDeleteEvent`]. + /// + /// [`GuildDeleteEvent`]: struct.GuildDeleteEvent.html + GuildDelete, + /// Indicator that a guild emojis update payload was received. + /// + /// This maps to [`GuildEmojisUpdateEvent`]. + /// + /// [`GuildEmojisUpdateEvent`]: struct.GuildEmojisUpdateEvent.html + GuildEmojisUpdate, + /// Indicator that a guild integrations update payload was received. + /// + /// This maps to [`GuildIntegrationsUpdateEvent`]. + /// + /// [`GuildIntegrationsUpdateEvent`]: struct.GuildIntegrationsUpdateEvent.html + GuildIntegrationsUpdate, + /// Indicator that a guild member add payload was received. + /// + /// This maps to [`GuildMemberAddEvent`]. + /// + /// [`GuildMemberAddEvent`]: struct.GuildMemberAddEvent.html + GuildMemberAdd, + /// Indicator that a guild member remove payload was received. + /// + /// This maps to [`GuildMemberRemoveEvent`]. + /// + /// [`GuildMemberRemoveEvent`]: struct.GuildMemberRemoveEvent.html + GuildMemberRemove, + /// Indicator that a guild member update payload was received. + /// + /// This maps to [`GuildMemberUpdateEvent`]. + /// + /// [`GuildMemberUpdateEvent`]: struct.GuildMemberUpdateEvent.html + GuildMemberUpdate, + /// Indicator that a guild members chunk payload was received. + /// + /// This maps to [`GuildMembersChunkEvent`]. + /// + /// [`GuildMembersChunkEvent`]: struct.GuildMembersChunkEvent.html + GuildMembersChunk, + /// Indicator that a guild role create payload was received. + /// + /// This maps to [`GuildRoleCreateEvent`]. + /// + /// [`GuildRoleCreateEvent`]: struct.GuildRoleCreateEvent.html + GuildRoleCreate, + /// Indicator that a guild role delete payload was received. + /// + /// This maps to [`GuildRoleDeleteEvent`]. + /// + /// [`GuildRoleDeleteEvent`]: struct.GuildRoleDeleteEvent.html + GuildRoleDelete, + /// Indicator that a guild role update payload was received. + /// + /// This maps to [`GuildRoleUpdateEvent`]. + /// + /// [`GuildRoleUpdateEvent`]: struct.GuildRoleUpdateEvent.html + GuildRoleUpdate, + /// Indicator that a guild unavailable payload was received. + /// + /// This maps to [`GuildUnavailableEvent`]. + /// + /// [`GuildUnavailableEvent`]: struct.GuildUnavailableEvent.html + GuildUnavailable, + /// Indicator that a guild update payload was received. + /// + /// This maps to [`GuildUpdateEvent`]. + /// + /// [`GuildUpdateEvent`]: struct.GuildUpdateEvent.html + GuildUpdate, + /// Indicator that a message create payload was received. + /// + /// This maps to [`MessageCreateEvent`]. + /// + /// [`MessageCreateEvent`]: struct.MessageCreateEvent.html + MessageCreate, + /// Indicator that a message delete payload was received. + /// + /// This maps to [`MessageDeleteEvent`]. + /// + /// [`MessageDeleteEvent`]: struct.MessageDeleteEvent.html + MessageDelete, + /// Indicator that a message delete bulk payload was received. + /// + /// This maps to [`MessageDeleteBulkEvent`]. + /// + /// [`MessageDeleteBulkEvent`]: struct.MessageDeleteBulkEvent.html + MessageDeleteBulk, + /// Indicator that a message update payload was received. + /// + /// This maps to [`MessageUpdateEvent`]. + /// + /// [`MessageUpdateEvent`]: struct.MessageUpdateEvent.html + MessageUpdate, + /// Indicator that a presence update payload was received. + /// + /// This maps to [`PresenceUpdateEvent`]. + /// + /// [`PresenceUpdateEvent`]: struct.PresenceUpdateEvent.html + PresenceUpdate, + /// Indicator that a presences replace payload was received. + /// + /// This maps to [`PresencesReplaceEvent`]. + /// + /// [`PresencesReplaceEvent`]: struct.PresencesReplaceEvent.html + PresencesReplace, + /// Indicator that a reaction add payload was received. + /// + /// This maps to [`ReactionAddEvent`]. + /// + /// [`ReactionAddEvent`]: struct.ReactionAddEvent.html + ReactionAdd, + /// Indicator that a reaction remove payload was received. + /// + /// This maps to [`ReactionRemoveEvent`]. + /// + /// [`ReactionRemoveEvent`]: struct.ResumedEvent.html + ReactionRemove, + /// Indicator that a reaction remove all payload was received. + /// + /// This maps to [`ReactionRemoveAllEvent`]. + /// + /// [`ReactionRemoveAllEvent`]: struct.ReactionRemoveAllEvent.html + ReactionRemoveAll, + /// Indicator that a ready payload was received. + /// + /// This maps to [`ReadyEvent`]. + /// + /// [`ReadyEvent`]: struct.ReadyEvent.html + Ready, + /// Indicator that a resumed payload was received. + /// + /// This maps to [`ResumedEvent`]. + /// + /// [`ResumedEvent`]: struct.ResumedEvent.html + Resumed, + /// Indicator that a typing start payload was received. + /// + /// This maps to [`TypingStartEvent`]. + /// + /// [`TypingStartEvent`]: struct.TypingStartEvent.html + TypingStart, + /// Indicator that a user update payload was received. + /// + /// This maps to [`UserUpdateEvent`]. + /// + /// [`UserUpdateEvent`]: struct.UserUpdateEvent.html + UserUpdate, + /// Indicator that a voice state payload was received. + /// + /// This maps to [`VoiceStateUpdateEvent`]. + /// + /// [`VoiceStateUpdateEvent`]: struct.VoiceStateUpdateEvent.html + VoiceStateUpdate, + /// Indicator that a voice server update payload was received. + /// + /// This maps to [`VoiceServerUpdateEvent`]. + /// + /// [`VoiceServerUpdateEvent`]: struct.VoiceServerUpdateEvent.html + VoiceServerUpdate, + /// Indicator that a webhook update payload was received. + /// + /// This maps to [`WebhookUpdateEvent`]. + /// + /// [`WebhookUpdateEvent`]: struct.WebhookUpdateEvent.html + WebhookUpdate, + /// An unknown event was received over the gateway. + /// + /// This should be logged so that support for it can be added in the + /// library. + Other(String), +} + +impl<'de> Deserialize<'de> for EventType { + fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error> + where D: Deserializer<'de> { + struct EventTypeVisitor; + + impl<'de> Visitor<'de> for EventTypeVisitor { + type Value = EventType; + + fn expecting(&self, f: &mut Formatter) -> FmtResult { + f.write_str("event type str") + } + + fn visit_str<E>(self, v: &str) -> StdResult<Self::Value, E> + where E: DeError { + Ok(match v { + "CHANNEL_CREATE" => EventType::ChannelCreate, + "CHANNEL_DELETE" => EventType::ChannelDelete, + "CHANNEL_PINS_UPDATE" => EventType::ChannelPinsUpdate, + "CHANNEL_RECIPIENT_ADD" => EventType::ChannelRecipientAdd, + "CHANNEL_RECIPIENT_REMOVE" => EventType::ChannelRecipientRemove, + "CHANNEL_UPDATE" => EventType::ChannelUpdate, + "GUILD_BAN_ADD" => EventType::GuildBanAdd, + "GUILD_BAN_REMOVE" => EventType::GuildBanRemove, + "GUILD_CREATE" => EventType::GuildCreate, + "GUILD_DELETE" => EventType::GuildDelete, + "GUILD_EMOJIS_UPDATE" => EventType::GuildEmojisUpdate, + "GUILD_INTEGRATIONS_UPDATE" => EventType::GuildIntegrationsUpdate, + "GUILD_MEMBER_ADD" => EventType::GuildMemberAdd, + "GUILD_MEMBER_REMOVE" => EventType::GuildMemberRemove, + "GUILD_MEMBER_UPDATE" => EventType::GuildMemberUpdate, + "GUILD_MEMBERS_CHUNK" => EventType::GuildMembersChunk, + "GUILD_ROLE_CREATE" => EventType::GuildRoleCreate, + "GUILD_ROLE_DELETE" => EventType::GuildRoleDelete, + "GUILD_ROLE_UPDATE" => EventType::GuildRoleUpdate, + "GUILD_UPDATE" => EventType::GuildUpdate, + "MESSAGE_CREATE" => EventType::MessageCreate, + "MESSAGE_DELETE" => EventType::MessageDelete, + "MESSAGE_DELETE_BULK" => EventType::MessageDeleteBulk, + "MESSAGE_REACTION_ADD" => EventType::ReactionAdd, + "MESSAGE_REACTION_REMOVE" => EventType::ReactionRemove, + "MESSAGE_REACTION_REMOVE_ALL" => EventType::ReactionRemoveAll, + "MESSAGE_UPDATE" => EventType::MessageUpdate, + "PRESENCE_UPDATE" => EventType::PresenceUpdate, + "PRESENCES_REPLACE" => EventType::PresencesReplace, + "READY" => EventType::Ready, + "RESUMED" => EventType::Resumed, + "TYPING_START" => EventType::TypingStart, + "USER_UPDATE" => EventType::UserUpdate, + "VOICE_SERVER_UPDATE" => EventType::VoiceServerUpdate, + "VOICE_STATE_UPDATE" => EventType::VoiceStateUpdate, + "WEBHOOKS_UPDATE" => EventType::WebhookUpdate, + other => EventType::Other(other.to_owned()), + }) + } + } + + deserializer.deserialize_str(EventTypeVisitor) } } @@ -1395,39 +1721,44 @@ pub enum VoiceEvent { Unknown(VoiceOpCode, Value), } -impl VoiceEvent { - #[cfg(feature = "voice")] - pub(crate) fn decode(value: Value) -> Result<VoiceEvent> { - let mut map = JsonMap::deserialize(value)?; +impl<'de> Deserialize<'de> for VoiceEvent { + fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error> + where D: Deserializer<'de> { + let mut map = JsonMap::deserialize(deserializer)?; - let op = match map.remove("op") { - Some(v) => VoiceOpCode::deserialize(v) - .map_err(JsonError::from) - .map_err(Error::from)?, - None => return Err(Error::Decode("expected voice event op", Value::Object(map))), - }; + let op = map.remove("op") + .ok_or_else(|| DeError::custom("expected voice event op")) + .and_then(VoiceOpCode::deserialize) + .map_err(DeError::custom)?; - let d = match map.remove("d") { - Some(v) => JsonMap::deserialize(v) - .map_err(JsonError::from) - .map_err(Error::from)?, - None => { - return Err(Error::Decode( - "expected voice gateway d", - Value::Object(map), - )) - }, - }; - let v = Value::Object(d); + let v = map.remove("d") + .ok_or_else(|| DeError::custom("expected voice gateway payload")) + .and_then(Value::deserialize) + .map_err(DeError::custom)?; Ok(match op { - VoiceOpCode::Heartbeat => VoiceEvent::Heartbeat(VoiceHeartbeat::deserialize(v)?), - VoiceOpCode::Hello => VoiceEvent::Hello(VoiceHello::deserialize(v)?), + VoiceOpCode::Heartbeat => { + let v = serde_json::from_value(v).map_err(DeError::custom)?; + + VoiceEvent::Heartbeat(v) + }, + VoiceOpCode::Hello => { + let v = VoiceHello::deserialize(v).map_err(DeError::custom)?; + + VoiceEvent::Hello(v) + }, VoiceOpCode::KeepAlive => VoiceEvent::KeepAlive, VoiceOpCode::SessionDescription => { - VoiceEvent::Ready(VoiceSessionDescription::deserialize(v)?) + let v = VoiceSessionDescription::deserialize(v) + .map_err(DeError::custom)?; + + VoiceEvent::Ready(v) + }, + VoiceOpCode::Speaking => { + let v = VoiceSpeaking::deserialize(v).map_err(DeError::custom)?; + + VoiceEvent::Speaking(v) }, - VoiceOpCode::Speaking => VoiceEvent::Speaking(VoiceSpeaking::deserialize(v)?), other => VoiceEvent::Unknown(other, v), }) } diff --git a/src/voice/connection.rs b/src/voice/connection.rs index 12c7eee..9b9c68f 100644 --- a/src/voice/connection.rs +++ b/src/voice/connection.rs @@ -12,6 +12,7 @@ use opus::{ Encoder as OpusEncoder, }; use parking_lot::Mutex; +use serde::Deserialize; use sodiumoxide::crypto::secretbox::{self, Key, Nonce}; use std::collections::HashMap; use std::io::Write; @@ -69,15 +70,19 @@ impl Connection { let url = generate_url(&mut info.endpoint)?; let mut client = ClientBuilder::from_url(&url).connect_secure(None)?; - client.send_json(&payload::build_identify(&info))?; let hello = loop { - match client.recv_json(VoiceEvent::decode)? { - Some(VoiceEvent::Hello(received_hello)) => { + let value = match client.recv_json()? { + Some(value) => value, + None => continue, + }; + + match VoiceEvent::deserialize(value)? { + VoiceEvent::Hello(received_hello) => { break received_hello; }, - Some(VoiceEvent::Heartbeat(_)) => continue, + VoiceEvent::Heartbeat(_) => continue, other => { debug!("[Voice] Expected hello/heartbeat; got: {:?}", other); @@ -382,8 +387,13 @@ fn generate_url(endpoint: &mut String) -> Result<WebsocketUrl> { #[inline] fn encryption_key(client: &mut Client) -> Result<Key> { loop { - match client.recv_json(VoiceEvent::decode)? { - Some(VoiceEvent::Ready(ready)) => { + let value = match client.recv_json()? { + Some(value) => value, + None => continue, + }; + + match VoiceEvent::deserialize(value)? { + VoiceEvent::Ready(ready) => { if ready.mode != CRYPTO_MODE { return Err(Error::Voice(VoiceError::VoiceModeInvalid)); } @@ -391,7 +401,7 @@ fn encryption_key(client: &mut Client) -> Result<Key> { return Key::from_slice(&ready.secret_key) .ok_or(Error::Voice(VoiceError::KeyGen)); }, - Some(VoiceEvent::Unknown(op, value)) => { + VoiceEvent::Unknown(op, value) => { debug!( "[Voice] Expected ready for key; got: op{}/v{:?}", op.num(), @@ -447,7 +457,16 @@ fn start_threads(client: Arc<Mutex<Client>>, udp: &UdpSocket) -> Result<ThreadIt let ws_thread = ThreadBuilder::new() .name(format!("{} WS", thread_name)) .spawn(move || loop { - while let Ok(Some(msg)) = client.lock().recv_json(VoiceEvent::decode) { + while let Ok(Some(value)) = client.lock().recv_json() { + let msg = match VoiceEvent::deserialize(value) { + Ok(msg) => msg, + Err(why) => { + warn!("Error deserializing voice event: {:?}", why); + + break; + }, + }; + if tx_clone.send(ReceiverStatus::Websocket(msg)).is_ok() { return; } |