Skip to content

Commit

Permalink
Use #[serde(remote = "Self")] to simplify serde impls (serenity-rs#…
Browse files Browse the repository at this point in the history
  • Loading branch information
kangalio authored and mkrasnitski committed Oct 24, 2023
1 parent 7728c14 commit 81fb975
Show file tree
Hide file tree
Showing 17 changed files with 147 additions and 520 deletions.
41 changes: 13 additions & 28 deletions src/model/application/interaction/application_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use crate::client::Context;
use crate::http::Http;
use crate::internal::prelude::*;
use crate::model::application::command::{CommandOptionType, CommandType};
use crate::model::application::interaction::add_guild_id_to_resolved;
use crate::model::channel::{Attachment, Message, PartialChannel};
use crate::model::guild::{Member, PartialMember, Role};
use crate::model::id::{
Expand All @@ -36,13 +35,13 @@ use crate::model::id::{
UserId,
};
use crate::model::user::User;
use crate::model::utils::{remove_from_map, remove_from_map_opt};
use crate::model::Permissions;

/// An interaction when a user invokes a slash command.
///
/// [Discord docs](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object).
#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "Self")]
#[non_exhaustive]
pub struct CommandInteraction {
/// Id of the interaction.
Expand Down Expand Up @@ -235,35 +234,20 @@ impl CommandInteraction {
}
}

// Manual impl needed to insert guild_id into resolved Role's
impl<'de> Deserialize<'de> for CommandInteraction {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let mut map = JsonMap::deserialize(deserializer)?;

let guild_id = remove_from_map_opt::<GuildId, _>(&mut map, "guild_id")?;

if let Some(guild_id) = guild_id {
add_guild_id_to_resolved(&mut map, guild_id);
let mut interaction = Self::deserialize(deserializer)?; // calls #[serde(remote)]-generated inherent method
if let Some(guild_id) = interaction.guild_id {
interaction.data.resolved.roles.values_mut().for_each(|r| r.guild_id = guild_id);
}
Ok(interaction)
}
}

let member = remove_from_map_opt::<Box<Member>, _>(&mut map, "member")?;
let user = remove_from_map_opt(&mut map, "user")?
.or_else(|| member.as_ref().map(|m| m.user.clone()))
.ok_or_else(|| DeError::custom("expected user or member"))?;

Ok(Self {
guild_id,
member,
user,
id: remove_from_map(&mut map, "id")?,
application_id: remove_from_map(&mut map, "application_id")?,
data: remove_from_map(&mut map, "data")?,
channel_id: remove_from_map(&mut map, "channel_id")?,
token: remove_from_map(&mut map, "token")?,
version: remove_from_map(&mut map, "version")?,
app_permissions: remove_from_map_opt(&mut map, "app_permissions")?,
locale: remove_from_map(&mut map, "locale")?,
guild_locale: remove_from_map_opt(&mut map, "guild_locale")?,
})
impl Serialize for CommandInteraction {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
Self::serialize(self, serializer) // calls #[serde(remote)]-generated inherent method
}
}

Expand Down Expand Up @@ -619,6 +603,7 @@ fn option_to_raw(option: &CommandDataOption) -> StdResult<RawCommandDataOption,
Ok(raw)
}

// Manual impl needed to emulate integer enum tags
impl<'de> Deserialize<'de> for CommandDataOption {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
option_from_raw(RawCommandDataOption::deserialize(deserializer)?)
Expand Down
44 changes: 15 additions & 29 deletions src/model/application/interaction/message_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use crate::client::Context;
use crate::http::Http;
use crate::internal::prelude::*;
use crate::model::application::component::ComponentType;
use crate::model::application::interaction::add_guild_id_to_resolved;
use crate::model::channel::Message;
use crate::model::guild::Member;
#[cfg(feature = "model")]
Expand All @@ -31,13 +30,13 @@ use crate::model::id::{
UserId,
};
use crate::model::user::User;
use crate::model::utils::{remove_from_map, remove_from_map_opt};
use crate::model::Permissions;

/// An interaction triggered by a message component.
///
/// [Discord docs](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure).
#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "Self")]
#[non_exhaustive]
pub struct ComponentInteraction {
/// Id of the interaction.
Expand Down Expand Up @@ -232,36 +231,22 @@ impl ComponentInteraction {
}
}

// Manual impl needed to insert guild_id into model data
impl<'de> Deserialize<'de> for ComponentInteraction {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let mut map = JsonMap::deserialize(deserializer)?;

let guild_id = remove_from_map_opt(&mut map, "guild_id")?;

if let Some(guild_id) = guild_id {
add_guild_id_to_resolved(&mut map, guild_id);
let mut interaction = Self::deserialize(deserializer)?; // calls #[serde(remote)]-generated inherent method
if let (Some(guild_id), Some(member)) = (interaction.guild_id, &mut interaction.member) {
member.guild_id = guild_id;
// If `member` is present, `user` wasn't sent and is still filled with default data
interaction.user = member.user.clone();
}
Ok(interaction)
}
}

let member = remove_from_map_opt::<Member, _>(&mut map, "member")?;
let user = remove_from_map_opt(&mut map, "user")?
.or_else(|| member.as_ref().map(|m| m.user.clone()))
.ok_or_else(|| DeError::custom("expected user or member"))?;

Ok(Self {
guild_id,
member,
user,
id: remove_from_map(&mut map, "id")?,
application_id: remove_from_map(&mut map, "application_id")?,
data: remove_from_map(&mut map, "data")?,
channel_id: remove_from_map(&mut map, "channel_id")?,
token: remove_from_map(&mut map, "token")?,
version: remove_from_map(&mut map, "version")?,
message: remove_from_map(&mut map, "message")?,
app_permissions: remove_from_map_opt(&mut map, "app_permissions")?,
locale: remove_from_map(&mut map, "locale")?,
guild_locale: remove_from_map_opt(&mut map, "guild_locale")?,
})
impl Serialize for ComponentInteraction {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
Self::serialize(self, serializer) // calls #[serde(remote)]-generated inherent method
}
}

Expand All @@ -276,6 +261,7 @@ pub enum ComponentInteractionDataKind {
Unknown(u8),
}

// Manual impl needed to emulate integer enum tags
impl<'de> Deserialize<'de> for ComponentInteractionDataKind {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
#[derive(Deserialize)]
Expand Down
23 changes: 2 additions & 21 deletions src/model/application/interaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use self::ping::PingInteraction;
use crate::internal::prelude::*;
use crate::json::from_value;
use crate::model::guild::PartialMember;
use crate::model::id::{ApplicationId, GuildId, InteractionId};
use crate::model::id::{ApplicationId, InteractionId};
use crate::model::user::User;
use crate::model::utils::deserialize_val;
use crate::model::Permissions;
Expand Down Expand Up @@ -217,6 +217,7 @@ impl Interaction {
}
}

// Manual impl needed to emulate integer enum tags
impl<'de> Deserialize<'de> for Interaction {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
let map = JsonMap::deserialize(deserializer)?;
Expand Down Expand Up @@ -302,23 +303,3 @@ pub struct MessageInteraction {
#[serde(skip_serializing_if = "Option::is_none")]
pub member: Option<PartialMember>,
}

fn add_guild_id_to_resolved(map: &mut JsonMap, guild_id: GuildId) {
if let Some(member) = map.get_mut("member").and_then(Value::as_object_mut) {
member.insert("guild_id".to_string(), guild_id.get().into());
}

if let Some(data) = map.get_mut("data") {
if let Some(resolved) = data.get_mut("resolved") {
if let Some(roles) = resolved.get_mut("roles") {
if let Some(values) = roles.as_object_mut() {
for value in values.values_mut() {
if let Some(role) = value.as_object_mut() {
role.insert("guild_id".to_string(), guild_id.get().into());
};
}
}
}
}
}
}
44 changes: 15 additions & 29 deletions src/model/application/interaction/modal.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use serde::de::{Deserialize, Deserializer, Error as DeError};
use serde::de::{Deserialize, Deserializer};
use serde::Serialize;

use super::add_guild_id_to_resolved;
#[cfg(feature = "model")]
use crate::builder::{
CreateInteractionResponse,
Expand All @@ -19,13 +18,13 @@ use crate::model::guild::Member;
use crate::model::id::MessageId;
use crate::model::id::{ApplicationId, ChannelId, GuildId, InteractionId};
use crate::model::user::User;
use crate::model::utils::{remove_from_map, remove_from_map_opt};
use crate::model::Permissions;

/// An interaction triggered by a modal submit.
///
/// [Discord docs](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object).
#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "Self")]
#[non_exhaustive]
pub struct ModalInteraction {
/// Id of the interaction.
Expand Down Expand Up @@ -195,35 +194,22 @@ impl ModalInteraction {
}
}

// Manual impl needed to insert guild_id into resolved Role's
impl<'de> Deserialize<'de> for ModalInteraction {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let mut map = JsonMap::deserialize(deserializer)?;

let guild_id = remove_from_map_opt::<GuildId, _>(&mut map, "guild_id")?;
if let Some(guild_id) = guild_id {
add_guild_id_to_resolved(&mut map, guild_id);
let mut interaction = Self::deserialize(deserializer)?; // calls #[serde(remote)]-generated inherent method
if let (Some(guild_id), Some(member)) = (interaction.guild_id, &mut interaction.member) {
member.guild_id = guild_id;
// If `member` is present, `user` wasn't sent and is still filled with default data
interaction.user = member.user.clone();
}
Ok(interaction)
}
}

let member = remove_from_map_opt::<Member, _>(&mut map, "member")?;
let user = remove_from_map_opt(&mut map, "user")?
.or_else(|| member.as_ref().map(|m| m.user.clone()))
.ok_or_else(|| DeError::custom("expected user or member"))?;

Ok(Self {
member,
user,
id: remove_from_map(&mut map, "id")?,
guild_id,
application_id: remove_from_map(&mut map, "application_id")?,
data: remove_from_map(&mut map, "data")?,
channel_id: remove_from_map(&mut map, "channel_id")?,
token: remove_from_map(&mut map, "token")?,
version: remove_from_map(&mut map, "version")?,
message: remove_from_map_opt(&mut map, "message")?,
app_permissions: remove_from_map_opt(&mut map, "app_permissions")?,
locale: remove_from_map(&mut map, "locale")?,
guild_locale: remove_from_map_opt(&mut map, "guild_locale")?,
})
impl Serialize for ModalInteraction {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
Self::serialize(self, serializer) // calls #[serde(remote)]-generated inherent method
}
}

Expand Down
1 change: 1 addition & 0 deletions src/model/channel/guild_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub struct GuildChannel {
///
/// The original voice channel has an Id equal to the guild's Id,
/// incremented by one.
#[serde(default)]
pub guild_id: GuildId,
/// The type of the channel.
#[serde(rename = "type")]
Expand Down
1 change: 1 addition & 0 deletions src/model/channel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ impl Channel {
}
}

// Manual impl needed to emulate integer enum tags
impl<'de> Deserialize<'de> for Channel {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let map = JsonMap::deserialize(deserializer)?;
Expand Down
Loading

0 comments on commit 81fb975

Please sign in to comment.