#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![warn(
clippy::missing_const_for_fn,
clippy::pedantic,
missing_docs,
unsafe_code
)]
#![allow(
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::unnecessary_wraps
)]
pub mod iter;
pub mod model;
pub mod traits;
#[cfg(feature = "permission-calculator")]
pub mod permission;
mod builder;
mod config;
mod event;
mod stats;
#[cfg(test)]
mod test;
pub use self::{
builder::InMemoryCacheBuilder,
config::{Config, ResourceType},
stats::InMemoryCacheStats,
traits::{
CacheableChannel, CacheableCurrentUser, CacheableEmoji, CacheableGuild,
CacheableGuildIntegration, CacheableMember, CacheableMessage, CacheableModels,
CacheablePresence, CacheableRole, CacheableStageInstance, CacheableSticker, CacheableUser,
CacheableVoiceState,
},
};
#[cfg(feature = "permission-calculator")]
pub use self::permission::InMemoryCachePermissions;
use self::iter::InMemoryCacheIter;
use dashmap::{
DashMap, DashSet,
mapref::{entry::Entry, one::Ref},
};
use std::{
collections::{HashSet, VecDeque},
fmt::{Debug, Formatter, Result as FmtResult},
hash::Hash,
ops::Deref,
sync::Mutex,
};
use twilight_model::{
channel::{Channel, StageInstance},
gateway::event::Event,
guild::{GuildIntegration, Role, scheduled_event::GuildScheduledEvent},
id::{
Id,
marker::{
ChannelMarker, EmojiMarker, GuildMarker, IntegrationMarker, MessageMarker, RoleMarker,
ScheduledEventMarker, StageMarker, StickerMarker, UserMarker,
},
},
user::{CurrentUser, User},
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct GuildResource<T> {
guild_id: Id<GuildMarker>,
value: T,
}
impl<T> GuildResource<T> {
pub const fn guild_id(&self) -> Id<GuildMarker> {
self.guild_id
}
pub const fn resource(&self) -> &T {
&self.value
}
}
impl<T> Deref for GuildResource<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.resource()
}
}
pub struct Reference<'a, K, V> {
inner: Ref<'a, K, V>,
}
impl<'a, K: Eq + Hash, V> Reference<'a, K, V> {
#[allow(clippy::missing_const_for_fn)]
fn new(inner: Ref<'a, K, V>) -> Self {
Self { inner }
}
pub fn key(&'a self) -> &'a K {
self.inner.key()
}
pub fn value(&'a self) -> &'a V {
self.inner.value()
}
}
impl<K: Eq + Hash, V: Debug> Debug for Reference<'_, K, V> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("Reference")
.field("inner", self.value())
.finish()
}
}
impl<K: Eq + Hash, V> Deref for Reference<'_, K, V> {
type Target = V;
fn deref(&self) -> &Self::Target {
self.value()
}
}
fn upsert_guild_item<K: Eq + Hash, V: PartialEq>(
map: &DashMap<K, GuildResource<V>>,
guild_id: Id<GuildMarker>,
key: K,
value: V,
) {
match map.entry(key) {
Entry::Occupied(entry) if entry.get().value == value => {}
Entry::Occupied(mut entry) => {
entry.insert(GuildResource { guild_id, value });
}
Entry::Vacant(entry) => {
entry.insert(GuildResource { guild_id, value });
}
}
}
#[allow(clippy::type_complexity)]
#[derive(Debug)]
pub struct InMemoryCache<CacheModels: CacheableModels = DefaultCacheModels> {
config: Config,
channels: DashMap<Id<ChannelMarker>, CacheModels::Channel>,
channel_messages: DashMap<Id<ChannelMarker>, VecDeque<Id<MessageMarker>>>,
current_user: Mutex<Option<CacheModels::CurrentUser>>,
emojis: DashMap<Id<EmojiMarker>, GuildResource<CacheModels::Emoji>>,
guilds: DashMap<Id<GuildMarker>, CacheModels::Guild>,
guild_channels: DashMap<Id<GuildMarker>, HashSet<Id<ChannelMarker>>>,
guild_emojis: DashMap<Id<GuildMarker>, HashSet<Id<EmojiMarker>>>,
guild_integrations: DashMap<Id<GuildMarker>, HashSet<Id<IntegrationMarker>>>,
guild_members: DashMap<Id<GuildMarker>, HashSet<Id<UserMarker>>>,
guild_presences: DashMap<Id<GuildMarker>, HashSet<Id<UserMarker>>>,
guild_roles: DashMap<Id<GuildMarker>, HashSet<Id<RoleMarker>>>,
guild_scheduled_events: DashMap<Id<GuildMarker>, HashSet<Id<ScheduledEventMarker>>>,
guild_stage_instances: DashMap<Id<GuildMarker>, HashSet<Id<StageMarker>>>,
guild_stickers: DashMap<Id<GuildMarker>, HashSet<Id<StickerMarker>>>,
integrations: DashMap<
(Id<GuildMarker>, Id<IntegrationMarker>),
GuildResource<CacheModels::GuildIntegration>,
>,
members: DashMap<(Id<GuildMarker>, Id<UserMarker>), CacheModels::Member>,
messages: DashMap<Id<MessageMarker>, CacheModels::Message>,
presences: DashMap<(Id<GuildMarker>, Id<UserMarker>), CacheModels::Presence>,
roles: DashMap<Id<RoleMarker>, GuildResource<CacheModels::Role>>,
scheduled_events:
DashMap<Id<ScheduledEventMarker>, GuildResource<CacheModels::GuildScheduledEvent>>,
stage_instances: DashMap<Id<StageMarker>, GuildResource<CacheModels::StageInstance>>,
stickers: DashMap<Id<StickerMarker>, GuildResource<CacheModels::Sticker>>,
unavailable_guilds: DashSet<Id<GuildMarker>>,
users: DashMap<Id<UserMarker>, CacheModels::User>,
user_guilds: DashMap<Id<UserMarker>, HashSet<Id<GuildMarker>>>,
#[allow(clippy::type_complexity)]
voice_state_channels: DashMap<Id<ChannelMarker>, HashSet<(Id<GuildMarker>, Id<UserMarker>)>>,
voice_state_guilds: DashMap<Id<GuildMarker>, HashSet<Id<UserMarker>>>,
voice_states: DashMap<(Id<GuildMarker>, Id<UserMarker>), CacheModels::VoiceState>,
}
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub struct DefaultCacheModels;
impl CacheableModels for DefaultCacheModels {
type Channel = Channel;
type CurrentUser = CurrentUser;
type Emoji = model::CachedEmoji;
type Guild = model::CachedGuild;
type GuildIntegration = GuildIntegration;
type Member = model::CachedMember;
type Message = model::CachedMessage;
type Presence = model::CachedPresence;
type Role = Role;
type StageInstance = StageInstance;
type Sticker = model::CachedSticker;
type User = User;
type VoiceState = model::CachedVoiceState;
type GuildScheduledEvent = GuildScheduledEvent;
}
pub type DefaultInMemoryCache = InMemoryCache<DefaultCacheModels>;
impl<CacheModels: CacheableModels> InMemoryCache<CacheModels> {
pub fn new() -> Self {
Self::default()
}
#[allow(clippy::type_complexity)]
pub const fn builder() -> InMemoryCacheBuilder<CacheModels> {
InMemoryCacheBuilder::new()
}
#[allow(clippy::missing_panics_doc)]
pub fn clear(&self) {
self.channels.clear();
self.channel_messages.clear();
self.current_user
.lock()
.expect("current user poisoned")
.take();
self.emojis.clear();
self.guilds.clear();
self.guild_channels.clear();
self.guild_emojis.clear();
self.guild_integrations.clear();
self.guild_members.clear();
self.guild_presences.clear();
self.guild_roles.clear();
self.guild_stage_instances.clear();
self.guild_stickers.clear();
self.integrations.clear();
self.members.clear();
self.messages.clear();
self.presences.clear();
self.roles.clear();
self.stickers.clear();
self.unavailable_guilds.clear();
self.users.clear();
self.voice_state_channels.clear();
self.voice_state_guilds.clear();
self.voice_states.clear();
}
pub const fn config(&self) -> &Config {
&self.config
}
#[allow(clippy::iter_not_returning_iterator, clippy::type_complexity)]
pub const fn iter(&self) -> InMemoryCacheIter<'_, CacheModels> {
InMemoryCacheIter::new(self)
}
#[allow(clippy::type_complexity)]
pub const fn stats(&self) -> InMemoryCacheStats<'_, CacheModels> {
InMemoryCacheStats::new(self)
}
#[cfg(feature = "permission-calculator")]
#[allow(clippy::type_complexity)]
pub const fn permissions(&self) -> InMemoryCachePermissions<'_, CacheModels> {
InMemoryCachePermissions::new(self)
}
pub fn update(&self, value: &impl UpdateCache<CacheModels>) {
value.update(self);
}
#[allow(clippy::missing_panics_doc)]
pub fn current_user(&self) -> Option<CacheModels::CurrentUser> {
self.current_user
.lock()
.expect("current user poisoned")
.clone()
}
pub fn channel(
&self,
channel_id: Id<ChannelMarker>,
) -> Option<Reference<'_, Id<ChannelMarker>, CacheModels::Channel>> {
self.channels.get(&channel_id).map(Reference::new)
}
pub fn channel_messages(
&self,
channel_id: Id<ChannelMarker>,
) -> Option<Reference<'_, Id<ChannelMarker>, VecDeque<Id<MessageMarker>>>> {
self.channel_messages.get(&channel_id).map(Reference::new)
}
pub fn emoji(
&self,
emoji_id: Id<EmojiMarker>,
) -> Option<Reference<'_, Id<EmojiMarker>, GuildResource<CacheModels::Emoji>>> {
self.emojis.get(&emoji_id).map(Reference::new)
}
pub fn guild(
&self,
guild_id: Id<GuildMarker>,
) -> Option<Reference<'_, Id<GuildMarker>, CacheModels::Guild>> {
self.guilds.get(&guild_id).map(Reference::new)
}
pub fn guild_channels(
&self,
guild_id: Id<GuildMarker>,
) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<ChannelMarker>>>> {
self.guild_channels.get(&guild_id).map(Reference::new)
}
pub fn guild_emojis(
&self,
guild_id: Id<GuildMarker>,
) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<EmojiMarker>>>> {
self.guild_emojis.get(&guild_id).map(Reference::new)
}
pub fn guild_integrations(
&self,
guild_id: Id<GuildMarker>,
) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<IntegrationMarker>>>> {
self.guild_integrations.get(&guild_id).map(Reference::new)
}
pub fn guild_members(
&self,
guild_id: Id<GuildMarker>,
) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<UserMarker>>>> {
self.guild_members.get(&guild_id).map(Reference::new)
}
pub fn guild_presences(
&self,
guild_id: Id<GuildMarker>,
) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<UserMarker>>>> {
self.guild_presences.get(&guild_id).map(Reference::new)
}
pub fn guild_roles(
&self,
guild_id: Id<GuildMarker>,
) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<RoleMarker>>>> {
self.guild_roles.get(&guild_id).map(Reference::new)
}
pub fn scheduled_events(
&self,
guild_id: Id<GuildMarker>,
) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<ScheduledEventMarker>>>> {
self.guild_scheduled_events
.get(&guild_id)
.map(Reference::new)
}
pub fn guild_stage_instances(
&self,
guild_id: Id<GuildMarker>,
) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<StageMarker>>>> {
self.guild_stage_instances
.get(&guild_id)
.map(Reference::new)
}
pub fn guild_stickers(
&self,
guild_id: Id<GuildMarker>,
) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<StickerMarker>>>> {
self.guild_stickers.get(&guild_id).map(Reference::new)
}
pub fn guild_voice_states(
&self,
guild_id: Id<GuildMarker>,
) -> Option<Reference<'_, Id<GuildMarker>, HashSet<Id<UserMarker>>>> {
self.voice_state_guilds.get(&guild_id).map(Reference::new)
}
#[allow(clippy::type_complexity)]
pub fn integration(
&self,
guild_id: Id<GuildMarker>,
integration_id: Id<IntegrationMarker>,
) -> Option<
Reference<
'_,
(Id<GuildMarker>, Id<IntegrationMarker>),
GuildResource<CacheModels::GuildIntegration>,
>,
> {
self.integrations
.get(&(guild_id, integration_id))
.map(Reference::new)
}
#[allow(clippy::type_complexity)]
pub fn member(
&self,
guild_id: Id<GuildMarker>,
user_id: Id<UserMarker>,
) -> Option<Reference<'_, (Id<GuildMarker>, Id<UserMarker>), CacheModels::Member>> {
self.members.get(&(guild_id, user_id)).map(Reference::new)
}
pub fn message(
&self,
message_id: Id<MessageMarker>,
) -> Option<Reference<'_, Id<MessageMarker>, CacheModels::Message>> {
self.messages.get(&message_id).map(Reference::new)
}
#[allow(clippy::type_complexity)]
pub fn presence(
&self,
guild_id: Id<GuildMarker>,
user_id: Id<UserMarker>,
) -> Option<Reference<'_, (Id<GuildMarker>, Id<UserMarker>), CacheModels::Presence>> {
self.presences.get(&(guild_id, user_id)).map(Reference::new)
}
pub fn role(
&self,
role_id: Id<RoleMarker>,
) -> Option<Reference<'_, Id<RoleMarker>, GuildResource<CacheModels::Role>>> {
self.roles.get(&role_id).map(Reference::new)
}
pub fn scheduled_event(
&self,
event_id: Id<ScheduledEventMarker>,
) -> Option<
Reference<'_, Id<ScheduledEventMarker>, GuildResource<CacheModels::GuildScheduledEvent>>,
> {
self.scheduled_events.get(&event_id).map(Reference::new)
}
pub fn stage_instance(
&self,
stage_id: Id<StageMarker>,
) -> Option<Reference<'_, Id<StageMarker>, GuildResource<CacheModels::StageInstance>>> {
self.stage_instances.get(&stage_id).map(Reference::new)
}
pub fn sticker(
&self,
sticker_id: Id<StickerMarker>,
) -> Option<Reference<'_, Id<StickerMarker>, GuildResource<CacheModels::Sticker>>> {
self.stickers.get(&sticker_id).map(Reference::new)
}
pub fn user(
&self,
user_id: Id<UserMarker>,
) -> Option<Reference<'_, Id<UserMarker>, CacheModels::User>> {
self.users.get(&user_id).map(Reference::new)
}
pub fn user_guilds(
&self,
user_id: Id<UserMarker>,
) -> Option<Reference<'_, Id<UserMarker>, HashSet<Id<GuildMarker>>>> {
self.user_guilds.get(&user_id).map(Reference::new)
}
pub fn voice_channel_states(
&self,
channel_id: Id<ChannelMarker>,
) -> Option<VoiceChannelStates<'_, CacheModels::VoiceState>> {
let user_ids = self.voice_state_channels.get(&channel_id)?;
Some(VoiceChannelStates {
index: 0,
user_ids,
voice_states: &self.voice_states,
})
}
#[allow(clippy::type_complexity)]
pub fn voice_state(
&self,
user_id: Id<UserMarker>,
guild_id: Id<GuildMarker>,
) -> Option<Reference<'_, (Id<GuildMarker>, Id<UserMarker>), CacheModels::VoiceState>> {
self.voice_states
.get(&(guild_id, user_id))
.map(Reference::new)
}
pub fn member_highest_role(
&self,
guild_id: Id<GuildMarker>,
user_id: Id<UserMarker>,
) -> Option<Id<RoleMarker>> {
let member = self.members.get(&(guild_id, user_id))?;
let mut highest_role: Option<(i64, Id<RoleMarker>)> = None;
for role_id in member.roles() {
if let Some(role) = self.role(*role_id) {
if let Some((position, id)) = highest_role
&& (role.position() < position
|| (role.position() == position && role.id() > id))
{
continue;
}
highest_role = Some((role.position(), role.id()));
}
}
highest_role.map(|(_, id)| id)
}
fn new_with_config(config: Config) -> Self {
Self {
config,
..Self::default()
}
}
const fn wants(&self, resource_type: ResourceType) -> bool {
self.config.resource_types().contains(resource_type)
}
}
impl<CacheModels: CacheableModels> Default for InMemoryCache<CacheModels> {
fn default() -> Self {
Self {
channel_messages: DashMap::new(),
channels: DashMap::new(),
config: Config::default(),
current_user: Mutex::new(None),
emojis: 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(),
user_guilds: DashMap::new(),
users: DashMap::new(),
voice_state_channels: DashMap::new(),
voice_state_guilds: DashMap::new(),
voice_states: DashMap::new(),
}
}
}
mod private {
use twilight_model::gateway::{
event::Event,
payload::incoming::{
ChannelCreate, ChannelDelete, ChannelPinsUpdate, ChannelUpdate, GuildCreate,
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,
},
};
pub trait Sealed {}
impl Sealed for Event {}
impl Sealed for ChannelCreate {}
impl Sealed for ChannelDelete {}
impl Sealed for ChannelPinsUpdate {}
impl Sealed for ChannelUpdate {}
impl Sealed for GuildCreate {}
impl Sealed for GuildEmojisUpdate {}
impl Sealed for GuildDelete {}
impl Sealed for GuildStickersUpdate {}
impl Sealed for GuildUpdate {}
impl Sealed for IntegrationCreate {}
impl Sealed for IntegrationDelete {}
impl Sealed for IntegrationUpdate {}
impl Sealed for InteractionCreate {}
impl Sealed for MemberAdd {}
impl Sealed for MemberChunk {}
impl Sealed for MemberRemove {}
impl Sealed for MemberUpdate {}
impl Sealed for MessageCreate {}
impl Sealed for MessageDelete {}
impl Sealed for MessageDeleteBulk {}
impl Sealed for MessageUpdate {}
impl Sealed for PresenceUpdate {}
impl Sealed for ReactionAdd {}
impl Sealed for ReactionRemove {}
impl Sealed for ReactionRemoveAll {}
impl Sealed for ReactionRemoveEmoji {}
impl Sealed for Ready {}
impl Sealed for RoleCreate {}
impl Sealed for RoleDelete {}
impl Sealed for RoleUpdate {}
impl Sealed for StageInstanceCreate {}
impl Sealed for StageInstanceDelete {}
impl Sealed for StageInstanceUpdate {}
impl Sealed for ThreadCreate {}
impl Sealed for ThreadDelete {}
impl Sealed for ThreadListSync {}
impl Sealed for ThreadUpdate {}
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 {}
}
pub trait UpdateCache<CacheModels: CacheableModels>: private::Sealed {
#[allow(unused_variables, clippy::type_complexity)]
fn update(&self, cache: &InMemoryCache<CacheModels>) {}
}
pub struct VoiceChannelStates<'a, CachedVoiceState> {
index: usize,
#[allow(clippy::type_complexity)]
user_ids: Ref<'a, Id<ChannelMarker>, HashSet<(Id<GuildMarker>, Id<UserMarker>)>>,
voice_states: &'a DashMap<(Id<GuildMarker>, Id<UserMarker>), CachedVoiceState>,
}
impl<'a, CachedVoiceState> Iterator for VoiceChannelStates<'a, CachedVoiceState> {
type Item = Reference<'a, (Id<GuildMarker>, Id<UserMarker>), CachedVoiceState>;
fn next(&mut self) -> Option<Self::Item> {
while let Some((guild_id, user_id)) = self.user_ids.iter().nth(self.index) {
if let Some(voice_state) = self.voice_states.get(&(*guild_id, *user_id)) {
self.index += 1;
return Some(Reference::new(voice_state));
}
}
None
}
}
impl<CacheModels: CacheableModels> UpdateCache<CacheModels> for Event {
#[allow(clippy::explicit_deref_methods)]
fn update(&self, cache: &InMemoryCache<CacheModels>) {
match self {
Event::ChannelCreate(v) => cache.update(v.deref()),
Event::ChannelDelete(v) => cache.update(v.deref()),
Event::ChannelPinsUpdate(v) => cache.update(v),
Event::ChannelUpdate(v) => cache.update(v.deref()),
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()),
Event::IntegrationDelete(v) => cache.update(v),
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::MessageCreate(v) => cache.update(v.deref()),
Event::MessageDelete(v) => cache.update(v),
Event::MessageDeleteBulk(v) => cache.update(v),
Event::MessageUpdate(v) => cache.update(v.deref()),
Event::PresenceUpdate(v) => cache.update(v.deref()),
Event::ReactionAdd(v) => cache.update(v.deref()),
Event::ReactionRemove(v) => cache.update(v.deref()),
Event::ReactionRemoveAll(v) => cache.update(v),
Event::ReactionRemoveEmoji(v) => cache.update(v),
Event::Ready(v) => cache.update(v),
Event::RoleCreate(v) => cache.update(v),
Event::RoleDelete(v) => cache.update(v),
Event::RoleUpdate(v) => cache.update(v),
Event::StageInstanceCreate(v) => cache.update(v),
Event::StageInstanceDelete(v) => cache.update(v),
Event::StageInstanceUpdate(v) => cache.update(v),
Event::ThreadCreate(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()),
Event::AutoModerationActionExecution(_)
| Event::AutoModerationRuleCreate(_)
| Event::AutoModerationRuleDelete(_)
| Event::AutoModerationRuleUpdate(_)
| Event::BanAdd(_)
| Event::BanRemove(_)
| Event::CommandPermissionsUpdate(_)
| Event::EntitlementCreate(_)
| Event::EntitlementDelete(_)
| Event::EntitlementUpdate(_)
| Event::GatewayClose(_)
| Event::GatewayHeartbeat
| Event::GatewayHeartbeatAck
| Event::GatewayHello(_)
| Event::GatewayInvalidateSession(_)
| Event::GatewayReconnect
| Event::GuildAuditLogEntryCreate(_)
| Event::GuildIntegrationsUpdate(_)
| Event::InviteCreate(_)
| Event::InviteDelete(_)
| Event::MessagePollVoteAdd(_)
| Event::MessagePollVoteRemove(_)
| Event::RateLimited(_)
| Event::Resumed
| Event::ThreadMembersUpdate(_)
| Event::ThreadMemberUpdate(_)
| Event::TypingStart(_)
| Event::VoiceServerUpdate(_)
| Event::WebhooksUpdate(_) => {}
}
}
}
#[cfg(test)]
mod tests {
use crate::{DefaultInMemoryCache, test};
use twilight_model::guild::RoleColors;
use twilight_model::{
gateway::payload::incoming::RoleDelete,
guild::{Member, MemberFlags, Permissions, Role, RoleFlags},
id::Id,
util::Timestamp,
};
#[test]
fn syntax_update() {
let cache = DefaultInMemoryCache::new();
cache.update(&RoleDelete {
guild_id: Id::new(1),
role_id: Id::new(1),
});
}
#[test]
fn clear() {
let cache = DefaultInMemoryCache::new();
cache.cache_emoji(Id::new(1), test::emoji(Id::new(3), None));
cache.cache_member(Id::new(2), test::member(Id::new(2)));
cache.clear();
assert!(cache.emojis.is_empty());
assert!(cache.members.is_empty());
}
#[test]
fn highest_role() {
let joined_at = Some(Timestamp::from_secs(1_632_072_645).expect("non zero"));
let cache = DefaultInMemoryCache::new();
let guild_id = Id::new(1);
let user = test::user(Id::new(1));
let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
cache.cache_member(
guild_id,
Member {
avatar: None,
avatar_decoration_data: None,
banner: None,
communication_disabled_until: None,
deaf: false,
flags,
joined_at,
mute: false,
nick: None,
pending: false,
premium_since: None,
roles: vec![Id::new(1), Id::new(2)],
user,
},
);
cache.cache_roles(
guild_id,
vec![
Role {
#[allow(deprecated)]
color: 0,
colors: RoleColors {
primary_color: 0,
secondary_color: None,
tertiary_color: None,
},
hoist: false,
icon: None,
id: Id::new(1),
managed: false,
mentionable: false,
name: "test".to_owned(),
permissions: Permissions::empty(),
position: 0,
flags: RoleFlags::empty(),
tags: None,
unicode_emoji: None,
},
Role {
#[allow(deprecated)]
color: 0,
colors: RoleColors {
primary_color: 0,
secondary_color: None,
tertiary_color: None,
},
hoist: false,
icon: None,
id: Id::new(2),
managed: false,
mentionable: false,
name: "test".to_owned(),
permissions: Permissions::empty(),
position: 1,
flags: RoleFlags::empty(),
tags: None,
unicode_emoji: None,
},
],
);
assert_eq!(
cache.member_highest_role(guild_id, Id::new(1)),
Some(Id::new(2))
);
}
}