diff --git a/examples/cache-optimization/models/guild_scheduled_events.rs b/examples/cache-optimization/models/guild_scheduled_events.rs new file mode 100644 index 00000000000..d623c906596 --- /dev/null +++ b/examples/cache-optimization/models/guild_scheduled_events.rs @@ -0,0 +1,51 @@ +use twilight_cache_inmemory::traits::CacheableGuildScheduledEvent; +use twilight_model::{ + guild::scheduled_event::GuildScheduledEvent, + id::{ + marker::{GuildMarker, ScheduledEventMarker}, + Id, + }, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct MinimalGuildScheduledEvent { + guild_id: Id, + id: Id, + user_count: Option, +} + +impl From for MinimalGuildScheduledEvent { + fn from(event: GuildScheduledEvent) -> Self { + Self { + guild_id: event.guild_id, + id: event.id, + user_count: event.user_count, + } + } +} + +impl PartialEq for MinimalGuildScheduledEvent { + fn eq(&self, other: &GuildScheduledEvent) -> bool { + self.id == other.id + } +} + +impl CacheableGuildScheduledEvent for MinimalGuildScheduledEvent { + fn add_user( + &mut self, + _guild_id: Id, + _event_id: Id, + _user_id: Id, + ) { + self.user_count = self.user_count.map(|c| c.saturating_add(1)); + } + + fn remove_user( + &mut self, + _guild_id: Id, + _event_id: Id, + _user_id: Id, + ) { + self.user_count = self.user_count.map(|c| c.saturating_sub(1)); + } +} diff --git a/examples/cache-optimization/models/mod.rs b/examples/cache-optimization/models/mod.rs index a6be0d8d954..10288f1508f 100644 --- a/examples/cache-optimization/models/mod.rs +++ b/examples/cache-optimization/models/mod.rs @@ -8,6 +8,7 @@ pub mod current_user; pub mod emoji; pub mod guild; pub mod guild_integration; +pub mod guild_scheduled_events; pub mod member; pub mod message; pub mod presence; @@ -26,6 +27,7 @@ impl CacheableModels for CustomCacheModels { type Emoji = emoji::MinimalCachedEmoji; type Guild = guild::MinimalCachedGuild; type GuildIntegration = guild_integration::MinimalCachedGuildIntegration; + type GuildScheduledEvent = guild_scheduled_events::MinimalGuildScheduledEvent; type Member = member::MinimalCachedMember; type Message = message::MinimalCachedMessage; type Presence = presence::MinimalCachedPresence; diff --git a/twilight-cache-inmemory/src/config.rs b/twilight-cache-inmemory/src/config.rs index 79e171f8b54..550017c2d08 100644 --- a/twilight-cache-inmemory/src/config.rs +++ b/twilight-cache-inmemory/src/config.rs @@ -37,6 +37,8 @@ bitflags! { const INTEGRATION = 1 << 12; /// Information relating to guild stickers. const STICKER = 1 << 13; + /// Information relating to guild scheduled events. + const GUILD_SCHEDULED_EVENT = 1 << 14; } } diff --git a/twilight-cache-inmemory/src/event/guild.rs b/twilight-cache-inmemory/src/event/guild.rs index 8d8c9b15d44..88a281b19f1 100644 --- a/twilight-cache-inmemory/src/event/guild.rs +++ b/twilight-cache-inmemory/src/event/guild.rs @@ -51,7 +51,7 @@ impl InMemoryCache { } if self.wants(ResourceType::STICKER) { - self.guild_stage_instances.insert(guild.id, HashSet::new()); + self.guild_stickers.insert(guild.id, HashSet::new()); self.cache_stickers(guild.id, mem::take(&mut guild.stickers)); } @@ -65,6 +65,14 @@ impl InMemoryCache { self.cache_stage_instances(guild.id, mem::take(&mut guild.stage_instances)); } + if self.wants(ResourceType::GUILD_SCHEDULED_EVENT) { + self.guild_scheduled_events.insert(guild.id, HashSet::new()); + self.cache_guild_scheduled_events( + guild.id, + mem::take(&mut guild.guild_scheduled_events), + ); + } + if self.wants(ResourceType::GUILD) { let guild = CacheModels::Guild::from(guild); self.unavailable_guilds.remove(&guild.id()); @@ -295,6 +303,7 @@ mod tests { emojis: Vec::new(), explicit_content_filter: ExplicitContentFilter::AllMembers, features: vec![], + guild_scheduled_events: Vec::new(), icon: None, id: Id::new(123), joined_at: Some(Timestamp::from_secs(1_632_072_645).expect("non zero")), diff --git a/twilight-cache-inmemory/src/event/guild_scheduled_events.rs b/twilight-cache-inmemory/src/event/guild_scheduled_events.rs new file mode 100644 index 00000000000..4440a6d878e --- /dev/null +++ b/twilight-cache-inmemory/src/event/guild_scheduled_events.rs @@ -0,0 +1,160 @@ +use twilight_model::{ + gateway::payload::incoming::{ + GuildScheduledEventCreate, GuildScheduledEventDelete, GuildScheduledEventUpdate, + GuildScheduledEventUserAdd, GuildScheduledEventUserRemove, + }, + guild::scheduled_event::GuildScheduledEvent, + id::{marker::GuildMarker, Id}, +}; + +use crate::{ + traits::CacheableGuildScheduledEvent, CacheableModels, InMemoryCache, ResourceType, UpdateCache, +}; + +impl InMemoryCache { + pub(crate) fn cache_guild_scheduled_events( + &self, + guild_id: Id, + guild_scheduled_events: impl IntoIterator, + ) { + for event in guild_scheduled_events { + self.cache_guild_scheduled_event(guild_id, event); + } + } + + fn cache_guild_scheduled_event( + &self, + guild_id: Id, + guild_scheduled_event: GuildScheduledEvent, + ) { + self.guild_scheduled_events + .entry(guild_id) + .or_default() + .insert(guild_scheduled_event.id); + + crate::upsert_guild_item( + &self.scheduled_events, + guild_id, + guild_scheduled_event.id, + CacheModels::GuildScheduledEvent::from(guild_scheduled_event), + ); + } +} + +impl UpdateCache for GuildScheduledEventCreate { + fn update(&self, cache: &InMemoryCache) { + if !cache.wants(ResourceType::GUILD_SCHEDULED_EVENT) { + return; + } + + cache.cache_guild_scheduled_event(self.guild_id, self.0.clone()); + } +} + +impl UpdateCache for GuildScheduledEventDelete { + fn update(&self, cache: &InMemoryCache) { + if !cache.wants(ResourceType::GUILD_SCHEDULED_EVENT) { + return; + } + + if cache.scheduled_events.remove(&self.id).is_some() { + if let Some(mut events) = cache.guild_scheduled_events.get_mut(&self.guild_id) { + events.remove(&self.id); + } + } + } +} + +impl UpdateCache for GuildScheduledEventUpdate { + fn update(&self, cache: &InMemoryCache) { + if !cache.wants(ResourceType::GUILD_SCHEDULED_EVENT) { + return; + } + + cache.cache_guild_scheduled_event(self.guild_id, self.0.clone()); + } +} + +impl UpdateCache for GuildScheduledEventUserAdd { + fn update(&self, cache: &InMemoryCache) { + cache + .scheduled_events + .entry(self.guild_scheduled_event_id) + .and_modify(|event| { + event + .value + .add_user(self.guild_id, self.guild_scheduled_event_id, self.user_id); + }); + } +} + +impl UpdateCache for GuildScheduledEventUserRemove { + fn update(&self, cache: &InMemoryCache) { + cache + .scheduled_events + .entry(self.guild_scheduled_event_id) + .and_modify(|event| { + event + .value + .remove_user(self.guild_id, self.guild_scheduled_event_id, self.user_id); + }); + } +} + +#[cfg(test)] +mod tests { + use crate::{test, DefaultInMemoryCache}; + use twilight_model::{ + gateway::payload::incoming::{ + GuildScheduledEventCreate, GuildScheduledEventUserAdd, GuildScheduledEventUserRemove, + }, + id::Id, + }; + + #[test] + fn guild_event_on_event() { + let cache = DefaultInMemoryCache::new(); + + let id = Id::new(1); + let guild_id = Id::new(2); + let user_id = Id::new(3); + + cache.update(&GuildScheduledEventCreate(test::guild_scheduled_event( + id, + guild_id, + Some(41), + ))); + + assert_eq!( + 1, + cache.guild_scheduled_events.get(&guild_id).unwrap().len() + ); + assert_eq!(1, cache.scheduled_events.len()); + assert_eq!( + 41, + cache.scheduled_events.get(&id).unwrap().user_count.unwrap() + ); + + cache.update(&GuildScheduledEventUserAdd { + guild_id, + guild_scheduled_event_id: id, + user_id, + }); + + assert_eq!( + 42, + cache.scheduled_events.get(&id).unwrap().user_count.unwrap() + ); + + cache.update(&GuildScheduledEventUserRemove { + guild_id, + guild_scheduled_event_id: id, + user_id, + }); + + assert_eq!( + 41, + cache.scheduled_events.get(&id).unwrap().user_count.unwrap() + ); + } +} diff --git a/twilight-cache-inmemory/src/event/mod.rs b/twilight-cache-inmemory/src/event/mod.rs index f042e897591..60351c58ec6 100644 --- a/twilight-cache-inmemory/src/event/mod.rs +++ b/twilight-cache-inmemory/src/event/mod.rs @@ -1,6 +1,7 @@ pub mod channel; pub mod emoji; pub mod guild; +pub mod guild_scheduled_events; pub mod integration; pub mod interaction; pub mod member; diff --git a/twilight-cache-inmemory/src/lib.rs b/twilight-cache-inmemory/src/lib.rs index c0b939bd7d5..88337d488f9 100644 --- a/twilight-cache-inmemory/src/lib.rs +++ b/twilight-cache-inmemory/src/lib.rs @@ -57,11 +57,11 @@ use std::{ use twilight_model::{ channel::{Channel, StageInstance}, gateway::event::Event, - guild::{GuildIntegration, Role}, + guild::{scheduled_event::GuildScheduledEvent, GuildIntegration, Role}, id::{ marker::{ ChannelMarker, EmojiMarker, GuildMarker, IntegrationMarker, MessageMarker, RoleMarker, - StageMarker, StickerMarker, UserMarker, + ScheduledEventMarker, StageMarker, StickerMarker, UserMarker, }, Id, }, @@ -204,6 +204,7 @@ pub struct InMemoryCache { guild_members: DashMap, HashSet>>, guild_presences: DashMap, HashSet>>, guild_roles: DashMap, HashSet>>, + guild_scheduled_events: DashMap, HashSet>>, guild_stage_instances: DashMap, HashSet>>, guild_stickers: DashMap, HashSet>>, integrations: DashMap< @@ -214,6 +215,8 @@ pub struct InMemoryCache { messages: DashMap, CacheModels::Message>, presences: DashMap<(Id, Id), CacheModels::Presence>, roles: DashMap, GuildResource>, + scheduled_events: + DashMap, GuildResource>, stage_instances: DashMap, GuildResource>, stickers: DashMap, GuildResource>, unavailable_guilds: DashSet>, @@ -246,6 +249,7 @@ impl CacheableModels for DefaultCacheModels { type Sticker = model::CachedSticker; type User = User; type VoiceState = model::CachedVoiceState; + type GuildScheduledEvent = GuildScheduledEvent; } /// The default implementation of [`InMemoryCache`]. @@ -535,6 +539,20 @@ impl InMemoryCache { self.guild_roles.get(&guild_id).map(Reference::new) } + /// Gets the scheduled events in a guild. + /// + /// This requires the [`GUILDS`] intent. + /// + /// [`GUILDS`]: ::twilight_model::gateway::Intents::GUILDS + pub fn scheduled_events( + &self, + guild_id: Id, + ) -> Option, HashSet>>> { + self.guild_scheduled_events + .get(&guild_id) + .map(Reference::new) + } + /// Gets the set of stage instances in a guild. /// /// This requires the [`GUILDS`] intent. @@ -655,6 +673,20 @@ impl InMemoryCache { self.roles.get(&role_id).map(Reference::new) } + /// Gets a scheduled event by ID. + /// + /// This requires the [`GUILDS`] intent. + /// + /// [`GUILDS`]: ::twilight_model::gateway::Intents::GUILDS + pub fn scheduled_event( + &self, + event_id: Id, + ) -> Option< + Reference<'_, Id, GuildResource>, + > { + self.scheduled_events.get(&event_id).map(Reference::new) + } + /// Gets a stage instance by ID. /// /// This requires the [`GUILDS`] intent. @@ -798,30 +830,32 @@ impl InMemoryCache { impl Default for InMemoryCache { fn default() -> Self { Self { - config: Config::default(), - channels: DashMap::new(), channel_messages: DashMap::new(), + channels: DashMap::new(), + config: Config::default(), current_user: Mutex::new(None), emojis: DashMap::new(), - guilds: DashMap::new(), guild_channels: DashMap::new(), guild_emojis: DashMap::new(), guild_integrations: DashMap::new(), guild_members: DashMap::new(), guild_presences: DashMap::new(), guild_roles: DashMap::new(), + guild_scheduled_events: DashMap::new(), guild_stage_instances: DashMap::new(), guild_stickers: DashMap::new(), + guilds: DashMap::new(), integrations: DashMap::new(), members: DashMap::new(), messages: DashMap::new(), presences: DashMap::new(), roles: DashMap::new(), + scheduled_events: DashMap::new(), stage_instances: DashMap::new(), stickers: DashMap::new(), unavailable_guilds: DashSet::new(), - users: DashMap::new(), user_guilds: DashMap::new(), + users: DashMap::new(), voice_state_channels: DashMap::new(), voice_state_guilds: DashMap::new(), voice_states: DashMap::new(), @@ -834,13 +868,15 @@ mod private { event::Event, payload::incoming::{ ChannelCreate, ChannelDelete, ChannelPinsUpdate, ChannelUpdate, GuildCreate, - GuildDelete, GuildEmojisUpdate, GuildStickersUpdate, GuildUpdate, IntegrationCreate, - IntegrationDelete, IntegrationUpdate, InteractionCreate, MemberAdd, MemberChunk, - MemberRemove, MemberUpdate, MessageCreate, MessageDelete, MessageDeleteBulk, - MessageUpdate, PresenceUpdate, ReactionAdd, ReactionRemove, ReactionRemoveAll, - ReactionRemoveEmoji, Ready, RoleCreate, RoleDelete, RoleUpdate, StageInstanceCreate, - StageInstanceDelete, StageInstanceUpdate, ThreadCreate, ThreadDelete, ThreadListSync, - ThreadUpdate, UnavailableGuild, UserUpdate, VoiceStateUpdate, + GuildDelete, GuildEmojisUpdate, GuildScheduledEventCreate, GuildScheduledEventDelete, + GuildScheduledEventUpdate, GuildScheduledEventUserAdd, GuildScheduledEventUserRemove, + GuildStickersUpdate, GuildUpdate, IntegrationCreate, IntegrationDelete, + IntegrationUpdate, InteractionCreate, MemberAdd, MemberChunk, MemberRemove, + MemberUpdate, MessageCreate, MessageDelete, MessageDeleteBulk, MessageUpdate, + PresenceUpdate, ReactionAdd, ReactionRemove, ReactionRemoveAll, ReactionRemoveEmoji, + Ready, RoleCreate, RoleDelete, RoleUpdate, StageInstanceCreate, StageInstanceDelete, + StageInstanceUpdate, ThreadCreate, ThreadDelete, ThreadListSync, ThreadUpdate, + UnavailableGuild, UserUpdate, VoiceStateUpdate, }, }; @@ -887,6 +923,11 @@ mod private { impl Sealed for UnavailableGuild {} impl Sealed for UserUpdate {} impl Sealed for VoiceStateUpdate {} + impl Sealed for GuildScheduledEventCreate {} + impl Sealed for GuildScheduledEventDelete {} + impl Sealed for GuildScheduledEventUpdate {} + impl Sealed for GuildScheduledEventUserAdd {} + impl Sealed for GuildScheduledEventUserRemove {} } /// Implemented for dispatch events. @@ -935,6 +976,11 @@ impl UpdateCache for Event { Event::GuildCreate(v) => cache.update(v.deref()), Event::GuildDelete(v) => cache.update(v), Event::GuildEmojisUpdate(v) => cache.update(v), + Event::GuildScheduledEventCreate(v) => cache.update(v.deref()), + Event::GuildScheduledEventDelete(v) => cache.update(v.deref()), + Event::GuildScheduledEventUpdate(v) => cache.update(v.deref()), + Event::GuildScheduledEventUserAdd(v) => cache.update(v), + Event::GuildScheduledEventUserRemove(v) => cache.update(v), Event::GuildStickersUpdate(v) => cache.update(v), Event::GuildUpdate(v) => cache.update(v.deref()), Event::IntegrationCreate(v) => cache.update(v.deref()), @@ -942,9 +988,9 @@ impl UpdateCache for Event { Event::IntegrationUpdate(v) => cache.update(v.deref()), Event::InteractionCreate(v) => cache.update(v.deref()), Event::MemberAdd(v) => cache.update(v.deref()), + Event::MemberChunk(v) => cache.update(v), Event::MemberRemove(v) => cache.update(v), Event::MemberUpdate(v) => cache.update(v.deref()), - Event::MemberChunk(v) => cache.update(v), Event::MessageCreate(v) => cache.update(v.deref()), Event::MessageDelete(v) => cache.update(v), Event::MessageDeleteBulk(v) => cache.update(v), @@ -962,12 +1008,13 @@ impl UpdateCache for Event { Event::StageInstanceDelete(v) => cache.update(v), Event::StageInstanceUpdate(v) => cache.update(v), Event::ThreadCreate(v) => cache.update(v.deref()), - Event::ThreadUpdate(v) => cache.update(v.deref()), Event::ThreadDelete(v) => cache.update(v), Event::ThreadListSync(v) => cache.update(v), + Event::ThreadUpdate(v) => cache.update(v.deref()), Event::UnavailableGuild(v) => cache.update(v), Event::UserUpdate(v) => cache.update(v), Event::VoiceStateUpdate(v) => cache.update(v.deref()), + // Ignored events. Event::AutoModerationActionExecution(_) | Event::AutoModerationRuleCreate(_) @@ -987,11 +1034,6 @@ impl UpdateCache for Event { | Event::GatewayReconnect | Event::GuildAuditLogEntryCreate(_) | Event::GuildIntegrationsUpdate(_) - | Event::GuildScheduledEventCreate(_) - | Event::GuildScheduledEventDelete(_) - | Event::GuildScheduledEventUpdate(_) - | Event::GuildScheduledEventUserAdd(_) - | Event::GuildScheduledEventUserRemove(_) | Event::InviteCreate(_) | Event::InviteDelete(_) | Event::MessagePollVoteAdd(_) diff --git a/twilight-cache-inmemory/src/model/guild.rs b/twilight-cache-inmemory/src/model/guild.rs index 99794ff247b..3592d79e6e7 100644 --- a/twilight-cache-inmemory/src/model/guild.rs +++ b/twilight-cache-inmemory/src/model/guild.rs @@ -4,8 +4,9 @@ use serde::Serialize; use twilight_model::{ gateway::payload::incoming::GuildUpdate, guild::{ - AfkTimeout, DefaultMessageNotificationLevel, ExplicitContentFilter, Guild, GuildFeature, - MfaLevel, NSFWLevel, Permissions, PremiumTier, SystemChannelFlags, VerificationLevel, + scheduled_event::GuildScheduledEvent, AfkTimeout, DefaultMessageNotificationLevel, + ExplicitContentFilter, Guild, GuildFeature, MfaLevel, NSFWLevel, Permissions, PremiumTier, + SystemChannelFlags, VerificationLevel, }, id::{ marker::{ApplicationMarker, ChannelMarker, GuildMarker, UserMarker}, @@ -30,6 +31,7 @@ pub struct CachedGuild { pub(crate) discovery_splash: Option, pub(crate) explicit_content_filter: ExplicitContentFilter, pub(crate) features: Vec, + pub(crate) guild_scheduled_events: Vec, pub(crate) icon: Option, pub(crate) id: Id, pub(crate) joined_at: Option, @@ -41,8 +43,8 @@ pub struct CachedGuild { pub(crate) mfa_level: MfaLevel, pub(crate) name: String, pub(crate) nsfw_level: NSFWLevel, - pub(crate) owner_id: Id, pub(crate) owner: Option, + pub(crate) owner_id: Id, pub(crate) permissions: Option, pub(crate) preferred_locale: String, pub(crate) premium_progress_bar_enabled: bool, @@ -52,8 +54,8 @@ pub struct CachedGuild { pub(crate) rules_channel_id: Option>, pub(crate) safety_alerts_channel_id: Option>, pub(crate) splash: Option, - pub(crate) system_channel_id: Option>, pub(crate) system_channel_flags: SystemChannelFlags, + pub(crate) system_channel_id: Option>, pub(crate) unavailable: bool, pub(crate) vanity_url_code: Option, pub(crate) verification_level: VerificationLevel, @@ -119,6 +121,11 @@ impl CachedGuild { } } + /// Scheduled guild events. + pub fn guild_scheduled_events(&self) -> &[GuildScheduledEvent] { + &self.guild_scheduled_events + } + /// Icon hash. /// /// See [Discord Docs/Image Formatting]. @@ -292,6 +299,7 @@ impl From for CachedGuild { discovery_splash, explicit_content_filter, features, + guild_scheduled_events, icon, id, joined_at, @@ -303,8 +311,8 @@ impl From for CachedGuild { mfa_level, name, nsfw_level, - owner_id, owner, + owner_id, permissions, preferred_locale, premium_progress_bar_enabled, @@ -334,6 +342,7 @@ impl From for CachedGuild { discovery_splash, explicit_content_filter, features, + guild_scheduled_events, icon, id, joined_at, @@ -345,8 +354,8 @@ impl From for CachedGuild { mfa_level, name, nsfw_level, - owner_id, owner, + owner_id, permissions, preferred_locale, premium_progress_bar_enabled, @@ -356,8 +365,8 @@ impl From for CachedGuild { rules_channel_id, safety_alerts_channel_id, splash, - system_channel_id, system_channel_flags, + system_channel_id, unavailable, vanity_url_code, verification_level, diff --git a/twilight-cache-inmemory/src/permission.rs b/twilight-cache-inmemory/src/permission.rs index 9747e65cc00..e0733ff80bd 100644 --- a/twilight-cache-inmemory/src/permission.rs +++ b/twilight-cache-inmemory/src/permission.rs @@ -726,6 +726,7 @@ mod tests { emojis: Vec::new(), explicit_content_filter: ExplicitContentFilter::AllMembers, features: Vec::new(), + guild_scheduled_events: Vec::new(), icon: None, joined_at: None, large: false, diff --git a/twilight-cache-inmemory/src/test.rs b/twilight-cache-inmemory/src/test.rs index 68a1d3b7ece..7dd41cc27f2 100644 --- a/twilight-cache-inmemory/src/test.rs +++ b/twilight-cache-inmemory/src/test.rs @@ -12,12 +12,16 @@ use twilight_model::{ GatewayReaction, }, guild::{ + scheduled_event::{EntityType, GuildScheduledEvent, PrivacyLevel, Status}, AfkTimeout, DefaultMessageNotificationLevel, Emoji, ExplicitContentFilter, Guild, Member, MemberFlags, MfaLevel, NSFWLevel, PartialMember, Permissions, PremiumTier, Role, RoleFlags, SystemChannelFlags, VerificationLevel, }, id::{ - marker::{ChannelMarker, EmojiMarker, GuildMarker, RoleMarker, StickerMarker, UserMarker}, + marker::{ + ChannelMarker, EmojiMarker, GuildMarker, RoleMarker, ScheduledEventMarker, + StickerMarker, UserMarker, + }, Id, }, user::{CurrentUser, User}, @@ -399,6 +403,7 @@ pub fn guild(id: Id, member_count: Option) -> Guild { emojis: Vec::new(), explicit_content_filter: ExplicitContentFilter::None, features: Vec::new(), + guild_scheduled_events: Vec::new(), icon: None, id, joined_at: None, @@ -437,3 +442,28 @@ pub fn guild(id: Id, member_count: Option) -> Guild { widget_enabled: None, } } + +pub fn guild_scheduled_event( + id: Id, + guild_id: Id, + user_count: Option, +) -> GuildScheduledEvent { + GuildScheduledEvent { + channel_id: None, + creator: None, + creator_id: None, + description: None, + entity_id: None, + entity_metadata: None, + entity_type: EntityType::External, + guild_id, + id, + image: None, + name: "test".to_owned(), + privacy_level: PrivacyLevel::GuildOnly, + scheduled_end_time: None, + scheduled_start_time: Timestamp::from_secs(789).unwrap(), + status: Status::Completed, + user_count, + } +} diff --git a/twilight-cache-inmemory/src/traits.rs b/twilight-cache-inmemory/src/traits.rs index a069f246614..336f24d477c 100644 --- a/twilight-cache-inmemory/src/traits.rs +++ b/twilight-cache-inmemory/src/traits.rs @@ -34,9 +34,14 @@ use twilight_model::{ payload::incoming::{GuildUpdate, MemberUpdate, MessageUpdate}, presence::Presence, }, - guild::{Emoji, Guild, GuildIntegration, Member, PartialMember, Role}, + guild::{ + scheduled_event::GuildScheduledEvent, Emoji, Guild, GuildIntegration, Member, + PartialMember, Role, + }, id::{ - marker::{ChannelMarker, GuildMarker, RoleMarker, StickerMarker, UserMarker}, + marker::{ + ChannelMarker, GuildMarker, RoleMarker, ScheduledEventMarker, StickerMarker, UserMarker, + }, Id, }, user::{CurrentUser, User}, @@ -48,32 +53,34 @@ use twilight_model::{channel::permission_overwrite::PermissionOverwrite, guild:: /// Super-trait for the generic cached representations of Discord API models. pub trait CacheableModels: Clone + Debug { - /// The cached [`Member`] model representation. - type Member: CacheableMember; - /// The cached [`Role`] model representation. - type Role: CacheableRole; /// The cached [`Channel`] model representation. type Channel: CacheableChannel; - /// The cached [`Guild`] model representation. - type Guild: CacheableGuild; - /// The cached [`VoiceState`] model representation. - type VoiceState: CacheableVoiceState; - /// The cached [`Message`] model representation. - type Message: CacheableMessage; /// The cached [`CurrentUser`] model representation. type CurrentUser: CacheableCurrentUser; - /// The cached [`Sticker`] model representation. - type Sticker: CacheableSticker; /// The cached [`Emoji`] model representation. type Emoji: CacheableEmoji; + /// The cached [`Guild`] model representation. + type Guild: CacheableGuild; /// The cached [`GuildIntegration`] model representation. type GuildIntegration: CacheableGuildIntegration; + /// The cached [`GuildScheduledEvent` model representation. + type GuildScheduledEvent: CacheableGuildScheduledEvent; + /// The cached [`Member`] model representation. + type Member: CacheableMember; + /// The cached [`Message`] model representation. + type Message: CacheableMessage; /// The cached [`Presence`] model representation. type Presence: CacheablePresence; + /// The cached [`Role`] model representation. + type Role: CacheableRole; /// The cached [`StageInstance`] model representation. type StageInstance: CacheableStageInstance; + /// The cached [`Sticker`] model representation. + type Sticker: CacheableSticker; /// The cached [`User`] model representation. type User: CacheableUser; + /// The cached [`VoiceState`] model representation. + type VoiceState: CacheableVoiceState; } /// Trait for a generic cached representation of a [`Member`]. @@ -301,3 +308,44 @@ impl CacheableStageInstance for StageInstance {} pub trait CacheableUser: From + PartialEq + PartialEq + Clone + Debug {} impl CacheableUser for User {} + +/// Trait for a generic cached representation of a [`GuildScheduledEvent`]. +pub trait CacheableGuildScheduledEvent: + From + PartialEq + PartialEq + Clone + Debug +{ + /// Add a user to an event. + fn add_user( + &mut self, + guild_id: Id, + event_id: Id, + user_id: Id, + ); + + /// Remove a user from an event. + fn remove_user( + &mut self, + guild_id: Id, + event_id: Id, + user_id: Id, + ); +} + +impl CacheableGuildScheduledEvent for GuildScheduledEvent { + fn add_user( + &mut self, + _guild_id: Id, + _event_id: Id, + _user_id: Id, + ) { + self.user_count = self.user_count.map(|count| count.saturating_add(1)); + } + + fn remove_user( + &mut self, + _guild_id: Id, + _event_id: Id, + _user_id: Id, + ) { + self.user_count = self.user_count.map(|count| count.saturating_sub(1)); + } +} diff --git a/twilight-model/src/guild/mod.rs b/twilight-model/src/guild/mod.rs index e1ddf9a5b4a..6f36264837b 100644 --- a/twilight-model/src/guild/mod.rs +++ b/twilight-model/src/guild/mod.rs @@ -97,6 +97,9 @@ pub struct Guild { pub explicit_content_filter: ExplicitContentFilter, /// Enabled guild features pub features: Vec, + /// Scheduled guild events. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub guild_scheduled_events: Vec, pub icon: Option, pub id: Id, #[serde(skip_serializing_if = "Option::is_none")] @@ -177,6 +180,7 @@ impl<'de> Deserialize<'de> for Guild { Emojis, ExplicitContentFilter, Features, + GuildScheduledEvents, Icon, Id, JoinedAt, @@ -239,6 +243,7 @@ impl<'de> Deserialize<'de> for Guild { let mut emojis = None; let mut explicit_content_filter = None; let mut features = None; + let mut guild_scheduled_events = None; let mut icon = None::>; let mut id = None; let mut joined_at = None::>; @@ -261,18 +266,18 @@ impl<'de> Deserialize<'de> for Guild { let mut presences = None; let mut public_updates_channel_id = None::>; let mut roles = None; + let mut rules_channel_id = None::>; let mut safety_alerts_channel_id = None::>; let mut splash = None::>; let mut stage_instances = None::>; let mut stickers = None::>; - let mut system_channel_id = None::>; let mut system_channel_flags = None; + let mut system_channel_id = None::>; let mut threads = None::>; - let mut rules_channel_id = None::>; let mut unavailable = None; + let mut vanity_url_code = None::>; let mut verification_level = None; let mut voice_states = None::>; - let mut vanity_url_code = None::>; let mut widget_channel_id = None::>; let mut widget_enabled = None::>; @@ -381,6 +386,13 @@ impl<'de> Deserialize<'de> for Guild { features = Some(map.next_value()?); } + Field::GuildScheduledEvents => { + if guild_scheduled_events.is_some() { + return Err(DeError::duplicate_field("guild_scheduled_events")); + } + + guild_scheduled_events = Some(map.next_value()?); + } Field::Icon => { if icon.is_some() { return Err(DeError::duplicate_field("icon")); @@ -670,6 +682,7 @@ impl<'de> Deserialize<'de> for Guild { let description = description.unwrap_or_default(); let discovery_splash = discovery_splash.unwrap_or_default(); let emojis = emojis.unwrap_or_default(); + let guild_scheduled_events = guild_scheduled_events.unwrap_or_default(); let icon = icon.unwrap_or_default(); let large = large.unwrap_or_default(); let joined_at = joined_at.unwrap_or_default(); @@ -728,6 +741,7 @@ impl<'de> Deserialize<'de> for Guild { emojis, explicit_content_filter, features, + guild_scheduled_events, icon, id, joined_at, @@ -854,6 +868,7 @@ mod tests { emojis: Vec::new(), explicit_content_filter: ExplicitContentFilter::MembersWithoutRole, features: Vec::from([GuildFeature::Banner]), + guild_scheduled_events: Vec::new(), icon: Some(image_hash::ICON), id: Id::new(1), joined_at,