From 3b5cb3113f8a00281379487acecbde55f99c6b47 Mon Sep 17 00:00:00 2001 From: Valdemar Erk Date: Sun, 31 Mar 2024 23:04:31 +0200 Subject: [PATCH 1/4] fix(model): Deserialize unavailable guilds in GUILD_CREATE. This changes GuildCreate from being a tuple struct to being a enum that can either be available or unavailable, and containing either a full Guild or a UnavailableGuild. --- examples/gateway-request-members.rs | 2 +- twilight-cache-inmemory/src/event/guild.rs | 15 +++-- twilight-cache-inmemory/src/permission.rs | 8 +-- twilight-model/src/gateway/event/mod.rs | 2 +- .../gateway/payload/incoming/guild_create.rs | 62 +++++++++++++++---- 5 files changed, 67 insertions(+), 22 deletions(-) diff --git a/examples/gateway-request-members.rs b/examples/gateway-request-members.rs index 5fa6d21b34d..daa3fcc069e 100644 --- a/examples/gateway-request-members.rs +++ b/examples/gateway-request-members.rs @@ -22,7 +22,7 @@ async fn main() -> anyhow::Result<()> { match event { Event::GuildCreate(guild) => { // Let's request all of the guild's members for caching. - shard.command(&RequestGuildMembers::builder(guild.id).query("", None)); + shard.command(&RequestGuildMembers::builder(guild.id()).query("", None)); } Event::Ready(_) => { // You can also specify an individual member within a guild. diff --git a/twilight-cache-inmemory/src/event/guild.rs b/twilight-cache-inmemory/src/event/guild.rs index f8af791347e..0b5e08bdac4 100644 --- a/twilight-cache-inmemory/src/event/guild.rs +++ b/twilight-cache-inmemory/src/event/guild.rs @@ -136,7 +136,12 @@ impl InMemoryCache { impl UpdateCache for GuildCreate { fn update(&self, cache: &InMemoryCache) { - cache.cache_guild(self.0.clone()); + match self { + GuildCreate::Available(g) => cache.cache_guild(g.clone()), + GuildCreate::Unavailable(g) => { + cache.unavailable_guilds.insert(g.id); + } + } } } @@ -350,7 +355,7 @@ mod tests { let cache = DefaultInMemoryCache::new(); let guild = test::guild(Id::new(1), None); - cache.update(&GuildCreate(guild.clone())); + cache.update(&GuildCreate::Available(guild.clone())); let mutation = PartialGuild { id: guild.id, @@ -406,7 +411,7 @@ mod tests { let member = test::member(user_id); let guild = test::guild(guild_id, Some(1)); - cache.update(&GuildCreate(guild)); + cache.update(&GuildCreate::Available(guild)); cache.update(&MemberAdd { guild_id, member }); assert_eq!(cache.guild(guild_id).unwrap().member_count, Some(2)); @@ -425,7 +430,7 @@ mod tests { let mut guild = test::guild(guild_id, Some(1)); guild.members.push(member); - cache.update(&GuildCreate(guild.clone())); + cache.update(&GuildCreate::Available(guild.clone())); assert_eq!( 1, @@ -446,7 +451,7 @@ mod tests { ); assert!(cache.guild(guild_id).unwrap().unavailable); - cache.update(&GuildCreate(guild)); + cache.update(&GuildCreate::Available(guild)); assert_eq!( 1, diff --git a/twilight-cache-inmemory/src/permission.rs b/twilight-cache-inmemory/src/permission.rs index c917f7a9a46..374a48fe5f7 100644 --- a/twilight-cache-inmemory/src/permission.rs +++ b/twilight-cache-inmemory/src/permission.rs @@ -923,7 +923,7 @@ mod tests { let cache = DefaultInMemoryCache::new(); let permissions = cache.permissions(); - cache.update(&GuildCreate(base_guild())); + cache.update(&GuildCreate::Available(base_guild())); cache.update(&MemberAdd { guild_id: GUILD_ID, member: test::member(USER_ID), @@ -969,7 +969,7 @@ mod tests { let cache = DefaultInMemoryCache::new(); let permissions = cache.permissions(); - cache.update(&GuildCreate(base_guild())); + cache.update(&GuildCreate::Available(base_guild())); assert!(matches!( permissions.in_channel(USER_ID, CHANNEL_ID).unwrap_err().kind(), ChannelErrorType::ChannelUnavailable { channel_id: c_id } @@ -1030,7 +1030,7 @@ mod tests { fn owner() -> Result<(), Box> { let cache = DefaultInMemoryCache::new(); let permissions = cache.permissions(); - cache.update(&GuildCreate(base_guild())); + cache.update(&GuildCreate::Available(base_guild())); assert!(permissions.root(OWNER_ID, GUILD_ID)?.is_all()); @@ -1088,7 +1088,7 @@ mod tests { everyone_permissions, )]); - cache.update(&GuildCreate(guild)); + cache.update(&GuildCreate::Availableg(guild)); let mut member = test::member(USER_ID); member.communication_disabled_until = Some(in_future); cache.update(&MemberAdd { diff --git a/twilight-model/src/gateway/event/mod.rs b/twilight-model/src/gateway/event/mod.rs index 51fd785a117..26ef405f755 100644 --- a/twilight-model/src/gateway/event/mod.rs +++ b/twilight-model/src/gateway/event/mod.rs @@ -183,7 +183,7 @@ impl Event { Event::ChannelUpdate(e) => e.0.guild_id, Event::CommandPermissionsUpdate(e) => Some(e.0.guild_id), Event::GuildAuditLogEntryCreate(e) => e.0.guild_id, - Event::GuildCreate(e) => Some(e.0.id), + Event::GuildCreate(e) => Some(e.id()), Event::GuildDelete(e) => Some(e.id), Event::GuildEmojisUpdate(e) => Some(e.guild_id), Event::GuildIntegrationsUpdate(e) => Some(e.guild_id), diff --git a/twilight-model/src/gateway/payload/incoming/guild_create.rs b/twilight-model/src/gateway/payload/incoming/guild_create.rs index 7aada80dd0e..26e757a369c 100644 --- a/twilight-model/src/gateway/payload/incoming/guild_create.rs +++ b/twilight-model/src/gateway/payload/incoming/guild_create.rs @@ -1,20 +1,60 @@ -use crate::guild::Guild; +use crate::{ + guild::{Guild, UnavailableGuild}, + id::{marker::GuildMarker, Id}, +}; use serde::{Deserialize, Serialize}; -use std::ops::{Deref, DerefMut}; #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct GuildCreate(pub Guild); - -impl Deref for GuildCreate { - type Target = Guild; +#[serde(untagged)] +pub enum GuildCreate { + Available(Guild), + Unavailable(UnavailableGuild), +} - fn deref(&self) -> &Self::Target { - &self.0 +impl GuildCreate { + /// Extract guild id. + pub const fn id(&self) -> Id { + match self { + GuildCreate::Available(g) => g.id, + GuildCreate::Unavailable(g) => g.id, + } } } -impl DerefMut for GuildCreate { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 +#[cfg(test)] +mod tests { + use serde_test::Token; + + use crate::{guild::UnavailableGuild, id::Id}; + + use super::GuildCreate; + + #[test] + fn unavailable_guild() { + let expected = GuildCreate::Unavailable(UnavailableGuild { + id: Id::new(1234), + unavailable: true, + }); + + // Note: This looks a bit strange because it does not use + // Token::TupleVariant, this is because it will + // serialize back into a struct, and thus make it + // fails. This also tests that the enum is transparent + // for serde. + serde_test::assert_tokens( + &expected, + &[ + Token::Struct { + name: "UnavailableGuild", + len: 2, + }, + Token::Str("id"), + Token::NewtypeStruct { name: "Id" }, + Token::Str("1234"), + Token::Str("unavailable"), + Token::Bool(true), + Token::StructEnd, + ], + ); } } From 26e14fa7de2d64ef004401b9e7badf619e2ea6ec Mon Sep 17 00:00:00 2001 From: Valdemar Erk Date: Mon, 1 Apr 2024 09:56:50 +0200 Subject: [PATCH 2/4] resolved comments --- twilight-cache-inmemory/src/permission.rs | 2 +- .../src/gateway/payload/incoming/guild_create.rs | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/twilight-cache-inmemory/src/permission.rs b/twilight-cache-inmemory/src/permission.rs index 374a48fe5f7..6111745896e 100644 --- a/twilight-cache-inmemory/src/permission.rs +++ b/twilight-cache-inmemory/src/permission.rs @@ -1088,7 +1088,7 @@ mod tests { everyone_permissions, )]); - cache.update(&GuildCreate::Availableg(guild)); + cache.update(&GuildCreate::Available(guild)); let mut member = test::member(USER_ID); member.communication_disabled_until = Some(in_future); cache.update(&MemberAdd { diff --git a/twilight-model/src/gateway/payload/incoming/guild_create.rs b/twilight-model/src/gateway/payload/incoming/guild_create.rs index 26e757a369c..348d647a6dd 100644 --- a/twilight-model/src/gateway/payload/incoming/guild_create.rs +++ b/twilight-model/src/gateway/payload/incoming/guild_create.rs @@ -12,7 +12,7 @@ pub enum GuildCreate { } impl GuildCreate { - /// Extract guild id. + /// ID of the guild. pub const fn id(&self) -> Id { match self { GuildCreate::Available(g) => g.id, @@ -36,11 +36,8 @@ mod tests { unavailable: true, }); - // Note: This looks a bit strange because it does not use - // Token::TupleVariant, this is because it will - // serialize back into a struct, and thus make it - // fails. This also tests that the enum is transparent - // for serde. + // Note: serde(untagged) makes the enum transparent which is + // the reason we don't use the variant here. serde_test::assert_tokens( &expected, &[ From 3629630a3be6de683654329b5339ac43fbf6578f Mon Sep 17 00:00:00 2001 From: Valdemar Erk Date: Sat, 4 May 2024 11:00:48 +0200 Subject: [PATCH 3/4] add unavailable to available test --- twilight-cache-inmemory/src/event/guild.rs | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/twilight-cache-inmemory/src/event/guild.rs b/twilight-cache-inmemory/src/event/guild.rs index 0b5e08bdac4..74e8526248b 100644 --- a/twilight-cache-inmemory/src/event/guild.rs +++ b/twilight-cache-inmemory/src/event/guild.rs @@ -350,6 +350,37 @@ mod tests { Ok(()) } + #[test] + fn unavailable_available_guild() { + let cache = DefaultInMemoryCache::new(); + let guild = test::guild(Id::new(1), None); + + cache.update(&GuildCreate::Unavailable( + twilight_model::guild::UnavailableGuild { + id: guild.id, + unavailable: true, + }, + )); + assert!(cache.unavailable_guilds.get(&guild.id).is_some()); + + cache.update(&GuildCreate::Available(guild.clone())); + assert_eq!(*cache.guilds.get(&guild.id).unwrap(), guild); + assert!(cache.unavailable_guilds.get(&guild.id).is_none()); + + cache.update(&GuildCreate::Unavailable( + twilight_model::guild::UnavailableGuild { + id: guild.id, + unavailable: true, + }, + )); + assert!(cache.unavailable_guilds.get(&guild.id).is_some()); + assert!(cache.guilds.get(&guild.id).unwrap().unavailable); + + cache.update(&GuildCreate::Available(guild.clone())); + assert!(!cache.guilds.get(&guild.id).unwrap().unavailable); + assert!(cache.unavailable_guilds.get(&guild.id).is_none()); + } + #[test] fn guild_update() { let cache = DefaultInMemoryCache::new(); From ab6be28f105f0cc898786b99f110c7b00b92022d Mon Sep 17 00:00:00 2001 From: Valdemar Erk Date: Sat, 4 May 2024 11:12:52 +0200 Subject: [PATCH 4/4] fix bug that would cause the unavailable state to not be updated. --- twilight-cache-inmemory/src/event/guild.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/twilight-cache-inmemory/src/event/guild.rs b/twilight-cache-inmemory/src/event/guild.rs index 74e8526248b..8d8c9b15d44 100644 --- a/twilight-cache-inmemory/src/event/guild.rs +++ b/twilight-cache-inmemory/src/event/guild.rs @@ -139,7 +139,7 @@ impl UpdateCache for GuildCreate { match self { GuildCreate::Available(g) => cache.cache_guild(g.clone()), GuildCreate::Unavailable(g) => { - cache.unavailable_guilds.insert(g.id); + cache.unavailable_guild(g.id); } } }