Skip to content

Commit

Permalink
Replace manual deserialization of VoiceState with helper structs (#…
Browse files Browse the repository at this point in the history
…1714)

The `InterimMember` struct is used for the same purpose as it is used for
`GuildMembersChunkEvent`, deserialize it without a `GuildId` but then
assign the correct `GuildId` from the `VoiceState`.
  • Loading branch information
nickelc authored and arqunis committed Mar 15, 2022
1 parent 23ed9ea commit 14e747d
Showing 1 changed file with 35 additions and 234 deletions.
269 changes: 35 additions & 234 deletions src/model/voice.rs
Expand Up @@ -2,15 +2,10 @@

use std::fmt;

use serde::de::{self, Deserialize, Deserializer, IgnoredAny, MapAccess, Visitor};
use serde::de::{Deserialize, Deserializer};

use super::{
guild::Member,
id::{ChannelId, GuildId, RoleId, UserId},
user::User,
};
#[cfg(feature = "unstable_discord_api")]
use crate::model::permissions::Permissions;
use crate::model::guild::{InterimMember, Member};
use crate::model::id::{ChannelId, GuildId, UserId};
use crate::model::Timestamp;

/// Information about an available voice region.
Expand Down Expand Up @@ -75,238 +70,44 @@ impl fmt::Debug for VoiceState {
impl<'de> Deserialize<'de> for VoiceState {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "snake_case")]
enum Field {
ChannelId,
Deaf,
GuildId,
Member,
Mute,
SelfDeaf,
SelfMute,
SelfStream,
SelfVideo,
SessionId,
Suppress,
Token,
UserId,
RequestToSpeakTimestamp,
}

#[derive(Deserialize)]
#[non_exhaustive]
struct PartialMember {
struct InterimVoiceState {
channel_id: Option<ChannelId>,
deaf: bool,
joined_at: Option<Timestamp>,
guild_id: Option<GuildId>,
member: Option<InterimMember>,
mute: bool,
nick: Option<String>,
roles: Vec<RoleId>,
user: User,
#[serde(default)]
pending: bool,
premium_since: Option<Timestamp>,
#[cfg(feature = "unstable_discord_api")]
permissions: Option<Permissions>,
avatar: Option<String>,
communication_disabled_until: Option<Timestamp>,
self_deaf: bool,
self_mute: bool,
self_stream: Option<bool>,
self_video: bool,
session_id: String,
suppress: bool,
token: Option<String>,
user_id: UserId,
request_to_speak_timestamp: Option<Timestamp>,
}

struct VoiceStateVisitor;

impl<'de> Visitor<'de> for VoiceStateVisitor {
type Value = VoiceState;

fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("struct VoiceState")
}

fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<Self::Value, V::Error> {
let mut channel_id = None;
let mut deaf = None;
let mut guild_id = None;
let mut member = None;
let mut mute = None;
let mut self_deaf = None;
let mut self_mute = None;
let mut self_stream = None;
let mut self_video = None;
let mut session_id = None;
let mut suppress = None;
let mut token = None;
let mut user_id = None;
let mut request_to_speak_timestamp = None;
let mut state = InterimVoiceState::deserialize(deserializer)?;

loop {
let key = match map.next_key() {
Ok(Some(key)) => key,
Ok(None) => break,
Err(_) => {
map.next_value::<IgnoredAny>()?;
continue;
},
};

match key {
Field::ChannelId => {
if channel_id.is_some() {
return Err(de::Error::duplicate_field("channel_id"));
}
channel_id = map.next_value()?;
},
Field::Deaf => {
if deaf.is_some() {
return Err(de::Error::duplicate_field("deaf"));
}
deaf = Some(map.next_value()?);
},
Field::GuildId => {
if guild_id.is_some() {
return Err(de::Error::duplicate_field("guild_id"));
}
guild_id = map.next_value()?;
},
Field::Member => {
if member.is_some() {
return Err(de::Error::duplicate_field("member"));
}

let partial_member: Option<PartialMember> = map.next_value()?;
if let Some(partial_member) = partial_member {
member = Some(Member {
deaf: partial_member.deaf,
guild_id: GuildId(0),
joined_at: partial_member.joined_at,
mute: partial_member.mute,
nick: partial_member.nick,
roles: partial_member.roles,
user: partial_member.user,
pending: partial_member.pending,
premium_since: partial_member.premium_since,
#[cfg(feature = "unstable_discord_api")]
permissions: partial_member.permissions,
avatar: partial_member.avatar,
communication_disabled_until: partial_member
.communication_disabled_until,
});
}
},
Field::Mute => {
if mute.is_some() {
return Err(de::Error::duplicate_field("mute"));
}
mute = Some(map.next_value()?);
},
Field::SelfDeaf => {
if self_deaf.is_some() {
return Err(de::Error::duplicate_field("self_deaf"));
}
self_deaf = Some(map.next_value()?);
},
Field::SelfMute => {
if self_mute.is_some() {
return Err(de::Error::duplicate_field("self_mute"));
}
self_mute = Some(map.next_value()?);
},
Field::SelfStream => {
if self_stream.is_some() {
return Err(de::Error::duplicate_field("self_stream"));
}
self_stream = map.next_value()?;
},
Field::SelfVideo => {
if self_video.is_some() {
return Err(de::Error::duplicate_field("self_video"));
}
self_video = Some(map.next_value()?);
},
Field::SessionId => {
if session_id.is_some() {
return Err(de::Error::duplicate_field("session_id"));
}
session_id = Some(map.next_value()?);
},
Field::Suppress => {
if suppress.is_some() {
return Err(de::Error::duplicate_field("suppress"));
}
suppress = Some(map.next_value()?);
},
Field::Token => {
if token.is_some() {
return Err(de::Error::duplicate_field("token"));
}
token = map.next_value()?;
},
Field::UserId => {
if user_id.is_some() {
return Err(de::Error::duplicate_field("user_id"));
}
user_id = Some(map.next_value()?);
},
Field::RequestToSpeakTimestamp => {
if request_to_speak_timestamp.is_some() {
return Err(de::Error::duplicate_field(
"request_to_speak_timestamp",
));
}
request_to_speak_timestamp = Some(map.next_value()?);
},
}
}

let deaf = deaf.ok_or_else(|| de::Error::missing_field("deaf"))?;
let mute = mute.ok_or_else(|| de::Error::missing_field("mute"))?;
let self_deaf = self_deaf.ok_or_else(|| de::Error::missing_field("self_deaf"))?;
let self_mute = self_mute.ok_or_else(|| de::Error::missing_field("self_mute"))?;
let self_video =
self_video.ok_or_else(|| de::Error::missing_field("self_video"))?;
let session_id =
session_id.ok_or_else(|| de::Error::missing_field("session_id"))?;
let suppress = suppress.ok_or_else(|| de::Error::missing_field("suppress"))?;
let user_id = user_id.ok_or_else(|| de::Error::missing_field("user_id"))?;
let request_to_speak_timestamp = request_to_speak_timestamp.unwrap_or(None);

if let (Some(guild_id), Some(member)) = (guild_id, member.as_mut()) {
member.guild_id = guild_id;
}

Ok(VoiceState {
channel_id,
deaf,
guild_id,
member,
mute,
self_deaf,
self_mute,
self_stream,
self_video,
session_id,
suppress,
token,
user_id,
request_to_speak_timestamp,
})
}
if let (Some(guild_id), Some(member)) = (state.guild_id, state.member.as_mut()) {
member.guild_id = guild_id;
}

const FIELDS: &[&str] = &[
"channel_id",
"deaf",
"guild_id",
"member",
"mute",
"self_deaf",
"self_mute",
"self_stream",
"self_video",
"session_id",
"suppress",
"token",
"user_id",
"request_to_speak_timestamp",
];

deserializer.deserialize_struct("VoiceState", FIELDS, VoiceStateVisitor)
Ok(VoiceState {
channel_id: state.channel_id,
deaf: state.deaf,
guild_id: state.guild_id,
member: state.member.map(Member::from),
mute: state.mute,
self_deaf: state.self_deaf,
self_mute: state.self_mute,
self_stream: state.self_stream,
self_video: state.self_video,
session_id: state.session_id,
suppress: state.suppress,
token: state.token,
user_id: state.user_id,
request_to_speak_timestamp: state.request_to_speak_timestamp,
})
}
}

0 comments on commit 14e747d

Please sign in to comment.