aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorZeyla Hellyer <[email protected]>2017-11-19 09:53:59 -0800
committerZeyla Hellyer <[email protected]>2017-11-19 09:53:59 -0800
commitc3aa63faee8b3ae6d5126aa27a74876766c61e4c (patch)
tree560ae5fcf011a8bb7cbaa5e4a5fc3bb2ee89ee31 /src
parentAdd `model::Reaction::user` (diff)
downloadserenity-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.rs9
-rw-r--r--src/internal/ws_impl.rs24
-rw-r--r--src/model/event.rs629
-rw-r--r--src/voice/connection.rs35
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;
}