1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![doc = include_str!("../README.md")]
3#![warn(
4 clippy::missing_const_for_fn,
5 clippy::pedantic,
6 missing_docs,
7 unsafe_code
8)]
9#![allow(
10 clippy::module_name_repetitions,
11 clippy::must_use_candidate,
12 clippy::unnecessary_wraps
13)]
14
15pub mod iter;
16pub mod model;
17pub mod traits;
18
19#[cfg(feature = "permission-calculator")]
20pub mod permission;
21
22mod builder;
23mod config;
24mod event;
25mod stats;
26
27#[cfg(test)]
28mod test;
29
30pub use self::{
31 builder::InMemoryCacheBuilder,
32 config::{Config, ResourceType},
33 stats::InMemoryCacheStats,
34 traits::{
35 CacheableChannel, CacheableCurrentUser, CacheableEmoji, CacheableGuild,
36 CacheableGuildIntegration, CacheableMember, CacheableMessage, CacheableModels,
37 CacheablePresence, CacheableRole, CacheableStageInstance, CacheableSticker, CacheableUser,
38 CacheableVoiceState,
39 },
40};
41
42#[cfg(feature = "permission-calculator")]
43pub use self::permission::InMemoryCachePermissions;
44
45use self::iter::InMemoryCacheIter;
46use dashmap::{
47 mapref::{entry::Entry, one::Ref},
48 DashMap, DashSet,
49};
50use std::{
51 collections::{HashSet, VecDeque},
52 fmt::{Debug, Formatter, Result as FmtResult},
53 hash::Hash,
54 ops::Deref,
55 sync::Mutex,
56};
57use twilight_model::{
58 channel::{Channel, StageInstance},
59 gateway::event::Event,
60 guild::{scheduled_event::GuildScheduledEvent, GuildIntegration, Role},
61 id::{
62 marker::{
63 ChannelMarker, EmojiMarker, GuildMarker, IntegrationMarker, MessageMarker, RoleMarker,
64 ScheduledEventMarker, StageMarker, StickerMarker, UserMarker,
65 },
66 Id,
67 },
68 user::{CurrentUser, User},
69};
70
71#[derive(Clone, Debug, Eq, PartialEq)]
77pub struct GuildResource<T> {
78 guild_id: Id<GuildMarker>,
79 value: T,
80}
81
82impl<T> GuildResource<T> {
83 pub const fn guild_id(&self) -> Id<GuildMarker> {
85 self.guild_id
86 }
87
88 pub const fn resource(&self) -> &T {
90 &self.value
91 }
92}
93
94impl<T> Deref for GuildResource<T> {
95 type Target = T;
96
97 fn deref(&self) -> &Self::Target {
98 self.resource()
99 }
100}
101
102pub struct Reference<'a, K, V> {
105 inner: Ref<'a, K, V>,
106}
107
108impl<'a, K: Eq + Hash, V> Reference<'a, K, V> {
109 #[allow(clippy::missing_const_for_fn)]
111 fn new(inner: Ref<'a, K, V>) -> Self {
112 Self { inner }
113 }
114
115 pub fn key(&'a self) -> &'a K {
117 self.inner.key()
118 }
119
120 pub fn value(&'a self) -> &'a V {
122 self.inner.value()
123 }
124}
125
126impl<K: Eq + Hash, V: Debug> Debug for Reference<'_, K, V> {
127 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
128 f.debug_struct("Reference")
129 .field("inner", self.value())
130 .finish()
131 }
132}
133
134impl<K: Eq + Hash, V> Deref for Reference<'_, K, V> {
135 type Target = V;
136
137 fn deref(&self) -> &Self::Target {
138 self.value()
139 }
140}
141
142fn upsert_guild_item<K: Eq + Hash, V: PartialEq>(
143 map: &DashMap<K, GuildResource<V>>,
144 guild_id: Id<GuildMarker>,
145 key: K,
146 value: V,
147) {
148 match map.entry(key) {
149 Entry::Occupied(entry) if entry.get().value == value => {}
150 Entry::Occupied(mut entry) => {
151 entry.insert(GuildResource { guild_id, value });
152 }
153 Entry::Vacant(entry) => {
154 entry.insert(GuildResource { guild_id, value });
155 }
156 }
157}
158
159#[allow(clippy::type_complexity)]
192#[derive(Debug)]
193pub struct InMemoryCache<CacheModels: CacheableModels = DefaultCacheModels> {
194 config: Config,
195 channels: DashMap<Id<ChannelMarker>, CacheModels::Channel>,
196 channel_messages: DashMap<Id<ChannelMarker>, VecDeque<Id<MessageMarker>>>,
197 current_user: Mutex<Option<CacheModels::CurrentUser>>,
199 emojis: DashMap<Id<EmojiMarker>, GuildResource<CacheModels::Emoji>>,
200 guilds: DashMap<Id<GuildMarker>, CacheModels::Guild>,
201 guild_channels: DashMap<Id<GuildMarker>, HashSet<Id<ChannelMarker>>>,
202 guild_emojis: DashMap<Id<GuildMarker>, HashSet<Id<EmojiMarker>>>,
203 guild_integrations: DashMap<Id<GuildMarker>, HashSet<Id<IntegrationMarker>>>,
204 guild_members: DashMap<Id<GuildMarker>, HashSet<Id<UserMarker>>>,
205 guild_presences: DashMap<Id<GuildMarker>, HashSet<Id<UserMarker>>>,
206 guild_roles: DashMap<Id<GuildMarker>, HashSet<Id<RoleMarker>>>,
207 guild_scheduled_events: DashMap<Id<GuildMarker>, HashSet<Id<ScheduledEventMarker>>>,
208 guild_stage_instances: DashMap<Id<GuildMarker>, HashSet<Id<StageMarker>>>,
209 guild_stickers: DashMap<Id<GuildMarker>, HashSet<Id<StickerMarker>>>,
210 integrations: DashMap<
211 (Id<GuildMarker>, Id<IntegrationMarker>),
212 GuildResource<CacheModels::GuildIntegration>,
213 >,
214 members: DashMap<(Id<GuildMarker>, Id<UserMarker>), CacheModels::Member>,
215 messages: DashMap<Id<MessageMarker>, CacheModels::Message>,
216 presences: DashMap<(Id<GuildMarker>, Id<UserMarker>), CacheModels::Presence>,
217 roles: DashMap<Id<RoleMarker>, GuildResource<CacheModels::Role>>,
218 scheduled_events:
219 DashMap<Id<ScheduledEventMarker>, GuildResource<CacheModels::GuildScheduledEvent>>,
220 stage_instances: DashMap<Id<StageMarker>, GuildResource<CacheModels::StageInstance>>,
221 stickers: DashMap<Id<StickerMarker>, GuildResource<CacheModels::Sticker>>,
222 unavailable_guilds: DashSet<Id<GuildMarker>>,
223 users: DashMap<Id<UserMarker>, CacheModels::User>,
224 user_guilds: DashMap<Id<UserMarker>, HashSet<Id<GuildMarker>>>,
225 #[allow(clippy::type_complexity)]
227 voice_state_channels: DashMap<Id<ChannelMarker>, HashSet<(Id<GuildMarker>, Id<UserMarker>)>>,
228 voice_state_guilds: DashMap<Id<GuildMarker>, HashSet<Id<UserMarker>>>,
230 voice_states: DashMap<(Id<GuildMarker>, Id<UserMarker>), CacheModels::VoiceState>,
232}
233
234#[allow(missing_docs)]
235#[derive(Clone, Debug)]
236pub struct DefaultCacheModels;
237
238impl CacheableModels for DefaultCacheModels {
239 type Channel = Channel;
240 type CurrentUser = CurrentUser;
241 type Emoji = model::CachedEmoji;
242 type Guild = model::CachedGuild;
243 type GuildIntegration = GuildIntegration;
244 type Member = model::CachedMember;
245 type Message = model::CachedMessage;
246 type Presence = model::CachedPresence;
247 type Role = Role;
248 type StageInstance = StageInstance;
249 type Sticker = model::CachedSticker;
250 type User = User;
251 type VoiceState = model::CachedVoiceState;
252 type GuildScheduledEvent = GuildScheduledEvent;
253}
254
255pub type DefaultInMemoryCache = InMemoryCache<DefaultCacheModels>;
259
260impl<CacheModels: CacheableModels> InMemoryCache<CacheModels> {
261 pub fn new() -> Self {
276 Self::default()
277 }
278
279 #[allow(clippy::type_complexity)]
281 pub const fn builder() -> InMemoryCacheBuilder<CacheModels> {
282 InMemoryCacheBuilder::new()
283 }
284
285 #[allow(clippy::missing_panics_doc)]
289 pub fn clear(&self) {
290 self.channels.clear();
291 self.channel_messages.clear();
292 self.current_user
293 .lock()
294 .expect("current user poisoned")
295 .take();
296 self.emojis.clear();
297 self.guilds.clear();
298 self.guild_channels.clear();
299 self.guild_emojis.clear();
300 self.guild_integrations.clear();
301 self.guild_members.clear();
302 self.guild_presences.clear();
303 self.guild_roles.clear();
304 self.guild_stage_instances.clear();
305 self.guild_stickers.clear();
306 self.integrations.clear();
307 self.members.clear();
308 self.messages.clear();
309 self.presences.clear();
310 self.roles.clear();
311 self.stickers.clear();
312 self.unavailable_guilds.clear();
313 self.users.clear();
314 self.voice_state_channels.clear();
315 self.voice_state_guilds.clear();
316 self.voice_states.clear();
317 }
318
319 pub const fn config(&self) -> &Config {
321 &self.config
322 }
323
324 #[allow(clippy::iter_not_returning_iterator, clippy::type_complexity)]
345 pub const fn iter(&self) -> InMemoryCacheIter<'_, CacheModels> {
346 InMemoryCacheIter::new(self)
347 }
348
349 #[allow(clippy::type_complexity)]
365 pub const fn stats(&self) -> InMemoryCacheStats<'_, CacheModels> {
366 InMemoryCacheStats::new(self)
367 }
368
369 #[cfg(feature = "permission-calculator")]
399 #[allow(clippy::type_complexity)]
400 pub const fn permissions(&self) -> InMemoryCachePermissions<'_, CacheModels> {
401 InMemoryCachePermissions::new(self)
402 }
403
404 pub fn update(&self, value: &impl UpdateCache<CacheModels>) {
406 value.update(self);
407 }
408
409 #[allow(clippy::missing_panics_doc)]
411 pub fn current_user(&self) -> Option<CacheModels::CurrentUser> {
412 self.current_user
413 .lock()
414 .expect("current user poisoned")
415 .clone()
416 }
417
418 pub fn channel(
420 &self,
421 channel_id: Id<ChannelMarker>,
422 ) -> Option<Reference<'_, Id<ChannelMarker>, CacheModels::Channel>> {
423 self.channels.get(&channel_id).map(Reference::new)
424 }
425
426 pub fn channel_messages(
433 &self,
434 channel_id: Id<ChannelMarker>,
435 ) -> Option<Reference<'_, Id<ChannelMarker>, VecDeque<Id<MessageMarker>>>> {
436 self.channel_messages.get(&channel_id).map(Reference::new)
437 }
438
439 pub fn emoji(
445 &self,
446 emoji_id: Id<EmojiMarker>,
447 ) -> Option<Reference<'_, Id<EmojiMarker>, GuildResource<CacheModels::Emoji>>> {
448 self.emojis.get(&emoji_id).map(Reference::new)
449 }
450
451 pub fn guild(
457 &self,
458 guild_id: Id<GuildMarker>,
459 ) -> Option<Reference<'_, Id<GuildMarker>, CacheModels::Guild>> {
460 self.guilds.get(&guild_id).map(Reference::new)
461 }
462
463 pub fn guild_channels(
469 &self,
470 guild_id: Id<GuildMarker>,
471 ) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<ChannelMarker>>>> {
472 self.guild_channels.get(&guild_id).map(Reference::new)
473 }
474
475 pub fn guild_emojis(
483 &self,
484 guild_id: Id<GuildMarker>,
485 ) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<EmojiMarker>>>> {
486 self.guild_emojis.get(&guild_id).map(Reference::new)
487 }
488
489 pub fn guild_integrations(
496 &self,
497 guild_id: Id<GuildMarker>,
498 ) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<IntegrationMarker>>>> {
499 self.guild_integrations.get(&guild_id).map(Reference::new)
500 }
501
502 pub fn guild_members(
510 &self,
511 guild_id: Id<GuildMarker>,
512 ) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<UserMarker>>>> {
513 self.guild_members.get(&guild_id).map(Reference::new)
514 }
515
516 pub fn guild_presences(
524 &self,
525 guild_id: Id<GuildMarker>,
526 ) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<UserMarker>>>> {
527 self.guild_presences.get(&guild_id).map(Reference::new)
528 }
529
530 pub fn guild_roles(
536 &self,
537 guild_id: Id<GuildMarker>,
538 ) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<RoleMarker>>>> {
539 self.guild_roles.get(&guild_id).map(Reference::new)
540 }
541
542 pub fn scheduled_events(
548 &self,
549 guild_id: Id<GuildMarker>,
550 ) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<ScheduledEventMarker>>>> {
551 self.guild_scheduled_events
552 .get(&guild_id)
553 .map(Reference::new)
554 }
555
556 pub fn guild_stage_instances(
562 &self,
563 guild_id: Id<GuildMarker>,
564 ) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<StageMarker>>>> {
565 self.guild_stage_instances
566 .get(&guild_id)
567 .map(Reference::new)
568 }
569
570 pub fn guild_stickers(
580 &self,
581 guild_id: Id<GuildMarker>,
582 ) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<StickerMarker>>>> {
583 self.guild_stickers.get(&guild_id).map(Reference::new)
584 }
585
586 pub fn guild_voice_states(
593 &self,
594 guild_id: Id<GuildMarker>,
595 ) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<UserMarker>>>> {
596 self.voice_state_guilds.get(&guild_id).map(Reference::new)
597 }
598
599 #[allow(clippy::type_complexity)]
606 pub fn integration(
607 &self,
608 guild_id: Id<GuildMarker>,
609 integration_id: Id<IntegrationMarker>,
610 ) -> Option<
611 Reference<
612 '_,
613 (Id<GuildMarker>, Id<IntegrationMarker>),
614 GuildResource<CacheModels::GuildIntegration>,
615 >,
616 > {
617 self.integrations
618 .get(&(guild_id, integration_id))
619 .map(Reference::new)
620 }
621
622 #[allow(clippy::type_complexity)]
628 pub fn member(
629 &self,
630 guild_id: Id<GuildMarker>,
631 user_id: Id<UserMarker>,
632 ) -> Option<Reference<'_, (Id<GuildMarker>, Id<UserMarker>), CacheModels::Member>> {
633 self.members.get(&(guild_id, user_id)).map(Reference::new)
634 }
635
636 pub fn message(
644 &self,
645 message_id: Id<MessageMarker>,
646 ) -> Option<Reference<'_, Id<MessageMarker>, CacheModels::Message>> {
647 self.messages.get(&message_id).map(Reference::new)
648 }
649
650 #[allow(clippy::type_complexity)]
656 pub fn presence(
657 &self,
658 guild_id: Id<GuildMarker>,
659 user_id: Id<UserMarker>,
660 ) -> Option<Reference<'_, (Id<GuildMarker>, Id<UserMarker>), CacheModels::Presence>> {
661 self.presences.get(&(guild_id, user_id)).map(Reference::new)
662 }
663
664 pub fn role(
670 &self,
671 role_id: Id<RoleMarker>,
672 ) -> Option<Reference<'_, Id<RoleMarker>, GuildResource<CacheModels::Role>>> {
673 self.roles.get(&role_id).map(Reference::new)
674 }
675
676 pub fn scheduled_event(
682 &self,
683 event_id: Id<ScheduledEventMarker>,
684 ) -> Option<
685 Reference<'_, Id<ScheduledEventMarker>, GuildResource<CacheModels::GuildScheduledEvent>>,
686 > {
687 self.scheduled_events.get(&event_id).map(Reference::new)
688 }
689
690 pub fn stage_instance(
696 &self,
697 stage_id: Id<StageMarker>,
698 ) -> Option<Reference<'_, Id<StageMarker>, GuildResource<CacheModels::StageInstance>>> {
699 self.stage_instances.get(&stage_id).map(Reference::new)
700 }
701
702 pub fn sticker(
711 &self,
712 sticker_id: Id<StickerMarker>,
713 ) -> Option<Reference<'_, Id<StickerMarker>, GuildResource<CacheModels::Sticker>>> {
714 self.stickers.get(&sticker_id).map(Reference::new)
715 }
716
717 pub fn user(
723 &self,
724 user_id: Id<UserMarker>,
725 ) -> Option<Reference<'_, Id<UserMarker>, CacheModels::User>> {
726 self.users.get(&user_id).map(Reference::new)
727 }
728
729 pub fn user_guilds(
741 &self,
742 user_id: Id<UserMarker>,
743 ) -> Option<Reference<'_, Id<UserMarker>, HashSet<Id<GuildMarker>>>> {
744 self.user_guilds.get(&user_id).map(Reference::new)
745 }
746
747 pub fn voice_channel_states(
754 &self,
755 channel_id: Id<ChannelMarker>,
756 ) -> Option<VoiceChannelStates<'_, CacheModels::VoiceState>> {
757 let user_ids = self.voice_state_channels.get(&channel_id)?;
758
759 Some(VoiceChannelStates {
760 index: 0,
761 user_ids,
762 voice_states: &self.voice_states,
763 })
764 }
765
766 #[allow(clippy::type_complexity)]
773 pub fn voice_state(
774 &self,
775 user_id: Id<UserMarker>,
776 guild_id: Id<GuildMarker>,
777 ) -> Option<Reference<'_, (Id<GuildMarker>, Id<UserMarker>), CacheModels::VoiceState>> {
778 self.voice_states
779 .get(&(guild_id, user_id))
780 .map(Reference::new)
781 }
782
783 pub fn member_highest_role(
790 &self,
791 guild_id: Id<GuildMarker>,
792 user_id: Id<UserMarker>,
793 ) -> Option<Id<RoleMarker>> {
794 let member = self.members.get(&(guild_id, user_id))?;
795
796 let mut highest_role: Option<(i64, Id<RoleMarker>)> = None;
797
798 for role_id in member.roles() {
799 if let Some(role) = self.role(*role_id) {
800 if let Some((position, id)) = highest_role {
801 if role.position() < position || (role.position() == position && role.id() > id)
802 {
803 continue;
804 }
805 }
806
807 highest_role = Some((role.position(), role.id()));
808 }
809 }
810
811 highest_role.map(|(_, id)| id)
812 }
813
814 fn new_with_config(config: Config) -> Self {
815 Self {
816 config,
817 ..Self::default()
818 }
819 }
820
821 const fn wants(&self, resource_type: ResourceType) -> bool {
824 self.config.resource_types().contains(resource_type)
825 }
826}
827
828impl<CacheModels: CacheableModels> Default for InMemoryCache<CacheModels> {
831 fn default() -> Self {
832 Self {
833 channel_messages: DashMap::new(),
834 channels: DashMap::new(),
835 config: Config::default(),
836 current_user: Mutex::new(None),
837 emojis: DashMap::new(),
838 guild_channels: DashMap::new(),
839 guild_emojis: DashMap::new(),
840 guild_integrations: DashMap::new(),
841 guild_members: DashMap::new(),
842 guild_presences: DashMap::new(),
843 guild_roles: DashMap::new(),
844 guild_scheduled_events: DashMap::new(),
845 guild_stage_instances: DashMap::new(),
846 guild_stickers: DashMap::new(),
847 guilds: DashMap::new(),
848 integrations: DashMap::new(),
849 members: DashMap::new(),
850 messages: DashMap::new(),
851 presences: DashMap::new(),
852 roles: DashMap::new(),
853 scheduled_events: DashMap::new(),
854 stage_instances: DashMap::new(),
855 stickers: DashMap::new(),
856 unavailable_guilds: DashSet::new(),
857 user_guilds: DashMap::new(),
858 users: DashMap::new(),
859 voice_state_channels: DashMap::new(),
860 voice_state_guilds: DashMap::new(),
861 voice_states: DashMap::new(),
862 }
863 }
864}
865
866mod private {
867 use twilight_model::gateway::{
868 event::Event,
869 payload::incoming::{
870 ChannelCreate, ChannelDelete, ChannelPinsUpdate, ChannelUpdate, GuildCreate,
871 GuildDelete, GuildEmojisUpdate, GuildScheduledEventCreate, GuildScheduledEventDelete,
872 GuildScheduledEventUpdate, GuildScheduledEventUserAdd, GuildScheduledEventUserRemove,
873 GuildStickersUpdate, GuildUpdate, IntegrationCreate, IntegrationDelete,
874 IntegrationUpdate, InteractionCreate, MemberAdd, MemberChunk, MemberRemove,
875 MemberUpdate, MessageCreate, MessageDelete, MessageDeleteBulk, MessageUpdate,
876 PresenceUpdate, ReactionAdd, ReactionRemove, ReactionRemoveAll, ReactionRemoveEmoji,
877 Ready, RoleCreate, RoleDelete, RoleUpdate, StageInstanceCreate, StageInstanceDelete,
878 StageInstanceUpdate, ThreadCreate, ThreadDelete, ThreadListSync, ThreadUpdate,
879 UnavailableGuild, UserUpdate, VoiceStateUpdate,
880 },
881 };
882
883 pub trait Sealed {}
884
885 impl Sealed for Event {}
886 impl Sealed for ChannelCreate {}
887 impl Sealed for ChannelDelete {}
888 impl Sealed for ChannelPinsUpdate {}
889 impl Sealed for ChannelUpdate {}
890 impl Sealed for GuildCreate {}
891 impl Sealed for GuildEmojisUpdate {}
892 impl Sealed for GuildDelete {}
893 impl Sealed for GuildStickersUpdate {}
894 impl Sealed for GuildUpdate {}
895 impl Sealed for IntegrationCreate {}
896 impl Sealed for IntegrationDelete {}
897 impl Sealed for IntegrationUpdate {}
898 impl Sealed for InteractionCreate {}
899 impl Sealed for MemberAdd {}
900 impl Sealed for MemberChunk {}
901 impl Sealed for MemberRemove {}
902 impl Sealed for MemberUpdate {}
903 impl Sealed for MessageCreate {}
904 impl Sealed for MessageDelete {}
905 impl Sealed for MessageDeleteBulk {}
906 impl Sealed for MessageUpdate {}
907 impl Sealed for PresenceUpdate {}
908 impl Sealed for ReactionAdd {}
909 impl Sealed for ReactionRemove {}
910 impl Sealed for ReactionRemoveAll {}
911 impl Sealed for ReactionRemoveEmoji {}
912 impl Sealed for Ready {}
913 impl Sealed for RoleCreate {}
914 impl Sealed for RoleDelete {}
915 impl Sealed for RoleUpdate {}
916 impl Sealed for StageInstanceCreate {}
917 impl Sealed for StageInstanceDelete {}
918 impl Sealed for StageInstanceUpdate {}
919 impl Sealed for ThreadCreate {}
920 impl Sealed for ThreadDelete {}
921 impl Sealed for ThreadListSync {}
922 impl Sealed for ThreadUpdate {}
923 impl Sealed for UnavailableGuild {}
924 impl Sealed for UserUpdate {}
925 impl Sealed for VoiceStateUpdate {}
926 impl Sealed for GuildScheduledEventCreate {}
927 impl Sealed for GuildScheduledEventDelete {}
928 impl Sealed for GuildScheduledEventUpdate {}
929 impl Sealed for GuildScheduledEventUserAdd {}
930 impl Sealed for GuildScheduledEventUserRemove {}
931}
932
933pub trait UpdateCache<CacheModels: CacheableModels>: private::Sealed {
937 #[allow(unused_variables, clippy::type_complexity)]
940 fn update(&self, cache: &InMemoryCache<CacheModels>) {}
941}
942
943pub struct VoiceChannelStates<'a, CachedVoiceState> {
945 index: usize,
946 #[allow(clippy::type_complexity)]
947 user_ids: Ref<'a, Id<ChannelMarker>, HashSet<(Id<GuildMarker>, Id<UserMarker>)>>,
948 voice_states: &'a DashMap<(Id<GuildMarker>, Id<UserMarker>), CachedVoiceState>,
949}
950
951impl<'a, CachedVoiceState> Iterator for VoiceChannelStates<'a, CachedVoiceState> {
952 type Item = Reference<'a, (Id<GuildMarker>, Id<UserMarker>), CachedVoiceState>;
953
954 fn next(&mut self) -> Option<Self::Item> {
955 while let Some((guild_id, user_id)) = self.user_ids.iter().nth(self.index) {
956 if let Some(voice_state) = self.voice_states.get(&(*guild_id, *user_id)) {
957 self.index += 1;
958
959 return Some(Reference::new(voice_state));
960 }
961 }
962
963 None
964 }
965}
966
967impl<CacheModels: CacheableModels> UpdateCache<CacheModels> for Event {
968 #[allow(clippy::explicit_deref_methods)]
970 fn update(&self, cache: &InMemoryCache<CacheModels>) {
971 match self {
972 Event::ChannelCreate(v) => cache.update(v.deref()),
973 Event::ChannelDelete(v) => cache.update(v.deref()),
974 Event::ChannelPinsUpdate(v) => cache.update(v),
975 Event::ChannelUpdate(v) => cache.update(v.deref()),
976 Event::GuildCreate(v) => cache.update(v.deref()),
977 Event::GuildDelete(v) => cache.update(v),
978 Event::GuildEmojisUpdate(v) => cache.update(v),
979 Event::GuildScheduledEventCreate(v) => cache.update(v.deref()),
980 Event::GuildScheduledEventDelete(v) => cache.update(v.deref()),
981 Event::GuildScheduledEventUpdate(v) => cache.update(v.deref()),
982 Event::GuildScheduledEventUserAdd(v) => cache.update(v),
983 Event::GuildScheduledEventUserRemove(v) => cache.update(v),
984 Event::GuildStickersUpdate(v) => cache.update(v),
985 Event::GuildUpdate(v) => cache.update(v.deref()),
986 Event::IntegrationCreate(v) => cache.update(v.deref()),
987 Event::IntegrationDelete(v) => cache.update(v),
988 Event::IntegrationUpdate(v) => cache.update(v.deref()),
989 Event::InteractionCreate(v) => cache.update(v.deref()),
990 Event::MemberAdd(v) => cache.update(v.deref()),
991 Event::MemberChunk(v) => cache.update(v),
992 Event::MemberRemove(v) => cache.update(v),
993 Event::MemberUpdate(v) => cache.update(v.deref()),
994 Event::MessageCreate(v) => cache.update(v.deref()),
995 Event::MessageDelete(v) => cache.update(v),
996 Event::MessageDeleteBulk(v) => cache.update(v),
997 Event::MessageUpdate(v) => cache.update(v.deref()),
998 Event::PresenceUpdate(v) => cache.update(v.deref()),
999 Event::ReactionAdd(v) => cache.update(v.deref()),
1000 Event::ReactionRemove(v) => cache.update(v.deref()),
1001 Event::ReactionRemoveAll(v) => cache.update(v),
1002 Event::ReactionRemoveEmoji(v) => cache.update(v),
1003 Event::Ready(v) => cache.update(v.deref()),
1004 Event::RoleCreate(v) => cache.update(v),
1005 Event::RoleDelete(v) => cache.update(v),
1006 Event::RoleUpdate(v) => cache.update(v),
1007 Event::StageInstanceCreate(v) => cache.update(v),
1008 Event::StageInstanceDelete(v) => cache.update(v),
1009 Event::StageInstanceUpdate(v) => cache.update(v),
1010 Event::ThreadCreate(v) => cache.update(v.deref()),
1011 Event::ThreadDelete(v) => cache.update(v),
1012 Event::ThreadListSync(v) => cache.update(v),
1013 Event::ThreadUpdate(v) => cache.update(v.deref()),
1014 Event::UnavailableGuild(v) => cache.update(v),
1015 Event::UserUpdate(v) => cache.update(v),
1016 Event::VoiceStateUpdate(v) => cache.update(v.deref()),
1017
1018 Event::AutoModerationActionExecution(_)
1020 | Event::AutoModerationRuleCreate(_)
1021 | Event::AutoModerationRuleDelete(_)
1022 | Event::AutoModerationRuleUpdate(_)
1023 | Event::BanAdd(_)
1024 | Event::BanRemove(_)
1025 | Event::CommandPermissionsUpdate(_)
1026 | Event::EntitlementCreate(_)
1027 | Event::EntitlementDelete(_)
1028 | Event::EntitlementUpdate(_)
1029 | Event::GatewayClose(_)
1030 | Event::GatewayHeartbeat
1031 | Event::GatewayHeartbeatAck
1032 | Event::GatewayHello(_)
1033 | Event::GatewayInvalidateSession(_)
1034 | Event::GatewayReconnect
1035 | Event::GuildAuditLogEntryCreate(_)
1036 | Event::GuildIntegrationsUpdate(_)
1037 | Event::InviteCreate(_)
1038 | Event::InviteDelete(_)
1039 | Event::MessagePollVoteAdd(_)
1040 | Event::MessagePollVoteRemove(_)
1041 | Event::Resumed
1042 | Event::ThreadMembersUpdate(_)
1043 | Event::ThreadMemberUpdate(_)
1044 | Event::TypingStart(_)
1045 | Event::VoiceServerUpdate(_)
1046 | Event::WebhooksUpdate(_) => {}
1047 }
1048 }
1049}
1050
1051#[cfg(test)]
1052mod tests {
1053 use crate::{test, DefaultInMemoryCache};
1054 use twilight_model::{
1055 gateway::payload::incoming::RoleDelete,
1056 guild::{Member, MemberFlags, Permissions, Role, RoleFlags},
1057 id::Id,
1058 util::Timestamp,
1059 };
1060
1061 #[test]
1062 fn syntax_update() {
1063 let cache = DefaultInMemoryCache::new();
1064 cache.update(&RoleDelete {
1065 guild_id: Id::new(1),
1066 role_id: Id::new(1),
1067 });
1068 }
1069
1070 #[test]
1071 fn clear() {
1072 let cache = DefaultInMemoryCache::new();
1073 cache.cache_emoji(Id::new(1), test::emoji(Id::new(3), None));
1074 cache.cache_member(Id::new(2), test::member(Id::new(2)));
1075 cache.clear();
1076 assert!(cache.emojis.is_empty());
1077 assert!(cache.members.is_empty());
1078 }
1079
1080 #[test]
1081 fn highest_role() {
1082 let joined_at = Some(Timestamp::from_secs(1_632_072_645).expect("non zero"));
1083 let cache = DefaultInMemoryCache::new();
1084 let guild_id = Id::new(1);
1085 let user = test::user(Id::new(1));
1086 let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
1087 cache.cache_member(
1088 guild_id,
1089 Member {
1090 avatar: None,
1091 communication_disabled_until: None,
1092 deaf: false,
1093 flags,
1094 joined_at,
1095 mute: false,
1096 nick: None,
1097 pending: false,
1098 premium_since: None,
1099 roles: vec![Id::new(1), Id::new(2)],
1100 user,
1101 },
1102 );
1103
1104 cache.cache_roles(
1105 guild_id,
1106 vec![
1107 Role {
1108 color: 0,
1109 hoist: false,
1110 icon: None,
1111 id: Id::new(1),
1112 managed: false,
1113 mentionable: false,
1114 name: "test".to_owned(),
1115 permissions: Permissions::empty(),
1116 position: 0,
1117 flags: RoleFlags::empty(),
1118 tags: None,
1119 unicode_emoji: None,
1120 },
1121 Role {
1122 color: 0,
1123 hoist: false,
1124 icon: None,
1125 id: Id::new(2),
1126 managed: false,
1127 mentionable: false,
1128 name: "test".to_owned(),
1129 permissions: Permissions::empty(),
1130 position: 1,
1131 flags: RoleFlags::empty(),
1132 tags: None,
1133 unicode_emoji: None,
1134 },
1135 ],
1136 );
1137
1138 assert_eq!(
1139 cache.member_highest_role(guild_id, Id::new(1)),
1140 Some(Id::new(2))
1141 );
1142 }
1143}