serenity/cache/
mod.rs

1//! A cache containing data received from [`Shard`]s.
2//!
3//! Using the cache allows to avoid REST API requests via the [`http`] module where possible.
4//! Issuing too many requests will lead to ratelimits.
5//!
6//! # Use by Models
7//!
8//! Most models of Discord objects, such as the [`Message`], [`GuildChannel`], or [`Emoji`], have
9//! methods for interacting with that single instance. This feature is only compiled if the `model`
10//! feature is enabled. An example of this is [`Guild::edit`], which performs a check to ensure that
11//! the current user has the [Manage Guild] permission prior to actually performing the HTTP
12//! request. The cache is involved due to the function's use of unlocking the cache and retrieving
13//! the permissions of the current user. This is an inexpensive method of being able to access data
14//! required by these sugary methods.
15//!
16//! # Do I need the Cache?
17//!
18//! If you're asking this, the answer is likely "definitely yes" or "definitely no"; any in-between
19//! tends to be "yes". If you are low on RAM, and need to run on only a couple MB, then the answer
20//! is "definitely no". If you do not care about RAM and want your bot to be able to access data
21//! while needing to hit the REST API as little as possible, then the answer is "yes".
22//!
23//! [`Shard`]: crate::gateway::Shard
24//! [`http`]: crate::http
25//! [Manage Guild]: Permissions::MANAGE_GUILD
26
27use std::collections::{HashSet, VecDeque};
28use std::hash::Hash;
29#[cfg(feature = "temp_cache")]
30use std::sync::Arc;
31#[cfg(feature = "temp_cache")]
32use std::time::Duration;
33
34use dashmap::mapref::entry::Entry;
35use dashmap::mapref::one::{MappedRef, Ref};
36use dashmap::DashMap;
37#[cfg(feature = "temp_cache")]
38use mini_moka::sync::Cache as MokaCache;
39use parking_lot::RwLock;
40use tracing::instrument;
41
42pub use self::cache_update::CacheUpdate;
43pub use self::settings::Settings;
44use crate::model::prelude::*;
45
46mod cache_update;
47mod event;
48mod settings;
49pub(crate) mod wrappers;
50
51#[cfg(feature = "temp_cache")]
52pub(crate) use wrappers::MaybeOwnedArc;
53use wrappers::{BuildHasher, MaybeMap, ReadOnlyMapRef};
54
55type MessageCache = DashMap<ChannelId, HashMap<MessageId, Message>, BuildHasher>;
56
57struct NotSend;
58
59enum CacheRefInner<'a, K, V, T> {
60    #[cfg(feature = "temp_cache")]
61    Arc(Arc<V>),
62    DashRef(Ref<'a, K, V, BuildHasher>),
63    DashMappedRef(MappedRef<'a, K, T, V, BuildHasher>),
64    ReadGuard(parking_lot::RwLockReadGuard<'a, V>),
65}
66
67pub struct CacheRef<'a, K, V, T = ()> {
68    inner: CacheRefInner<'a, K, V, T>,
69    phantom: std::marker::PhantomData<*const NotSend>,
70}
71
72impl<'a, K, V, T> CacheRef<'a, K, V, T> {
73    fn new(inner: CacheRefInner<'a, K, V, T>) -> Self {
74        Self {
75            inner,
76            phantom: std::marker::PhantomData,
77        }
78    }
79
80    #[cfg(feature = "temp_cache")]
81    fn from_arc(inner: MaybeOwnedArc<V>) -> Self {
82        Self::new(CacheRefInner::Arc(inner.get_inner()))
83    }
84
85    fn from_ref(inner: Ref<'a, K, V, BuildHasher>) -> Self {
86        Self::new(CacheRefInner::DashRef(inner))
87    }
88
89    fn from_mapped_ref(inner: MappedRef<'a, K, T, V, BuildHasher>) -> Self {
90        Self::new(CacheRefInner::DashMappedRef(inner))
91    }
92
93    fn from_guard(inner: parking_lot::RwLockReadGuard<'a, V>) -> Self {
94        Self::new(CacheRefInner::ReadGuard(inner))
95    }
96}
97
98impl<K: Eq + Hash, V, T> std::ops::Deref for CacheRef<'_, K, V, T> {
99    type Target = V;
100
101    fn deref(&self) -> &Self::Target {
102        match &self.inner {
103            #[cfg(feature = "temp_cache")]
104            CacheRefInner::Arc(inner) => inner,
105            CacheRefInner::DashRef(inner) => inner.value(),
106            CacheRefInner::DashMappedRef(inner) => inner.value(),
107            CacheRefInner::ReadGuard(inner) => inner,
108        }
109    }
110}
111
112type Never = std::convert::Infallible;
113type MappedGuildRef<'a, T> = CacheRef<'a, GuildId, T, Guild>;
114
115pub type MemberRef<'a> = MappedGuildRef<'a, Member>;
116pub type GuildRoleRef<'a> = MappedGuildRef<'a, Role>;
117pub type UserRef<'a> = CacheRef<'a, UserId, User, Never>;
118pub type GuildRef<'a> = CacheRef<'a, GuildId, Guild, Never>;
119pub type SettingsRef<'a> = CacheRef<'a, Never, Settings, Never>;
120pub type GuildChannelRef<'a> = MappedGuildRef<'a, GuildChannel>;
121pub type CurrentUserRef<'a> = CacheRef<'a, Never, CurrentUser, Never>;
122pub type GuildRolesRef<'a> = MappedGuildRef<'a, HashMap<RoleId, Role>>;
123pub type GuildChannelsRef<'a> = MappedGuildRef<'a, HashMap<ChannelId, GuildChannel>>;
124pub type MessageRef<'a> = CacheRef<'a, ChannelId, Message, HashMap<MessageId, Message>>;
125pub type ChannelMessagesRef<'a> = CacheRef<'a, ChannelId, HashMap<MessageId, Message>, Never>;
126
127#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
128#[derive(Debug)]
129pub(crate) struct CachedShardData {
130    pub total: u32,
131    pub connected: HashSet<ShardId>,
132    pub has_sent_shards_ready: bool,
133}
134
135/// A cache containing data received from [`Shard`]s.
136///
137/// Using the cache allows to avoid REST API requests via the [`http`] module where possible.
138/// Issuing too many requests will lead to ratelimits.
139///
140/// This is the list of cached resources and the events that populate them:
141/// - channels: [`ChannelCreateEvent`], [`ChannelUpdateEvent`], [`GuildCreateEvent`]
142/// - guilds: [`GuildCreateEvent`]
143/// - unavailable_guilds: [`ReadyEvent`], [`GuildDeleteEvent`]
144/// - users: [`GuildMemberAddEvent`], [`GuildMemberRemoveEvent`], [`GuildMembersChunkEvent`],
145///   [`PresenceUpdateEvent`], [`ReadyEvent`]
146/// - presences: [`PresenceUpdateEvent`], [`ReadyEvent`]
147/// - messages: [`MessageCreateEvent`]
148///
149/// The documentation of each event contains the required gateway intents.
150///
151/// [`Shard`]: crate::gateway::Shard
152/// [`http`]: crate::http
153#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
154#[derive(Debug)]
155#[non_exhaustive]
156pub struct Cache {
157    // Temp cache:
158    // ---
159    /// Cache of channels that have been fetched via to_channel.
160    ///
161    /// The TTL for each value is configured in CacheSettings.
162    #[cfg(feature = "temp_cache")]
163    pub(crate) temp_channels: MokaCache<ChannelId, MaybeOwnedArc<GuildChannel>, BuildHasher>,
164    /// Cache of private channels created via create_dm_channel.
165    ///
166    /// The TTL for each value is configured in CacheSettings.
167    #[cfg(feature = "temp_cache")]
168    pub(crate) temp_private_channels: MokaCache<UserId, MaybeOwnedArc<PrivateChannel>, BuildHasher>,
169    /// Cache of messages that have been fetched via message.
170    ///
171    /// The TTL for each value is configured in CacheSettings.
172    #[cfg(feature = "temp_cache")]
173    pub(crate) temp_messages: MokaCache<MessageId, MaybeOwnedArc<Message>, BuildHasher>,
174    /// Cache of users who have been fetched from `to_user`.
175    ///
176    /// The TTL for each value is configured in CacheSettings.
177    #[cfg(feature = "temp_cache")]
178    pub(crate) temp_users: MokaCache<UserId, MaybeOwnedArc<User>, BuildHasher>,
179
180    // Channels cache:
181    /// A map of channel ids to the guilds in which the channel data is stored.
182    pub(crate) channels: MaybeMap<ChannelId, GuildId>,
183
184    // Guilds cache:
185    // ---
186    /// A map of guilds with full data available. This includes data like [`Role`]s and [`Emoji`]s
187    /// that are not available through the REST API.
188    pub(crate) guilds: MaybeMap<GuildId, Guild>,
189    /// A list of guilds which are "unavailable".
190    ///
191    /// Additionally, guilds are always unavailable for bot users when a Ready is received. Guilds
192    /// are "sent in" over time through the receiving of [`Event::GuildCreate`]s.
193    pub(crate) unavailable_guilds: MaybeMap<GuildId, ()>,
194
195    // Users cache:
196    // ---
197    /// A map of users that the current user sees.
198    ///
199    /// Users are added to - and updated from - this map via the following received events:
200    ///
201    /// - [`GuildMemberAdd`][`GuildMemberAddEvent`]
202    /// - [`GuildMemberRemove`][`GuildMemberRemoveEvent`]
203    /// - [`GuildMembersChunk`][`GuildMembersChunkEvent`]
204    /// - [`PresenceUpdate`][`PresenceUpdateEvent`]
205    /// - [`Ready`][`ReadyEvent`]
206    ///
207    /// Note, however, that users are _not_ removed from the map on removal events such as
208    /// [`GuildMemberRemove`][`GuildMemberRemoveEvent`], as other structs such as members or
209    /// recipients may still exist.
210    pub(crate) users: MaybeMap<UserId, User>,
211
212    // Messages cache:
213    // ---
214    pub(crate) messages: MessageCache,
215    /// Queue of message IDs for each channel.
216    ///
217    /// This is simply a vecdeque so we can keep track of the order of messages inserted into the
218    /// cache. When a maximum number of messages are in a channel's cache, we can pop the front and
219    /// remove that ID from the cache.
220    pub(crate) message_queue: DashMap<ChannelId, VecDeque<MessageId>, BuildHasher>,
221
222    // Miscellanous fixed-size data
223    // ---
224    /// Information about running shards
225    pub(crate) shard_data: RwLock<CachedShardData>,
226    /// The current user "logged in" and for which events are being received for.
227    ///
228    /// The current user contains information that a regular [`User`] does not, such as whether it
229    /// is a bot, whether the user is verified, etc.
230    ///
231    /// Refer to the documentation for [`CurrentUser`] for more information.
232    pub(crate) user: RwLock<CurrentUser>,
233    /// The settings for the cache.
234    settings: RwLock<Settings>,
235}
236
237impl Cache {
238    /// Creates a new cache.
239    #[inline]
240    #[must_use]
241    pub fn new() -> Self {
242        Self::default()
243    }
244
245    /// Creates a new cache instance with settings applied.
246    ///
247    /// # Examples
248    ///
249    /// ```rust
250    /// use serenity::cache::{Cache, Settings};
251    ///
252    /// let mut settings = Settings::default();
253    /// settings.max_messages = 10;
254    ///
255    /// let cache = Cache::new_with_settings(settings);
256    /// ```
257    #[instrument]
258    pub fn new_with_settings(settings: Settings) -> Self {
259        #[cfg(feature = "temp_cache")]
260        fn temp_cache<K, V>(ttl: Duration) -> MokaCache<K, V, BuildHasher>
261        where
262            K: Hash + Eq + Send + Sync + 'static,
263            V: Clone + Send + Sync + 'static,
264        {
265            MokaCache::builder().time_to_live(ttl).build_with_hasher(BuildHasher::default())
266        }
267
268        Self {
269            #[cfg(feature = "temp_cache")]
270            temp_private_channels: temp_cache(settings.time_to_live),
271            #[cfg(feature = "temp_cache")]
272            temp_channels: temp_cache(settings.time_to_live),
273            #[cfg(feature = "temp_cache")]
274            temp_messages: temp_cache(settings.time_to_live),
275            #[cfg(feature = "temp_cache")]
276            temp_users: temp_cache(settings.time_to_live),
277
278            channels: MaybeMap(settings.cache_channels.then(DashMap::default)),
279
280            guilds: MaybeMap(settings.cache_guilds.then(DashMap::default)),
281            unavailable_guilds: MaybeMap(settings.cache_guilds.then(DashMap::default)),
282
283            users: MaybeMap(settings.cache_users.then(DashMap::default)),
284
285            messages: DashMap::default(),
286            message_queue: DashMap::default(),
287
288            shard_data: RwLock::new(CachedShardData {
289                total: 1,
290                connected: HashSet::new(),
291                has_sent_shards_ready: false,
292            }),
293            user: RwLock::new(CurrentUser::default()),
294            settings: RwLock::new(settings),
295        }
296    }
297
298    /// Fetches the number of [`Member`]s that have not had data received.
299    ///
300    /// The important detail to note here is that this is the number of _member_s that have not had
301    /// data received. A single [`User`] may have multiple associated member objects that have not
302    /// been received.
303    ///
304    /// This can be used in combination with [`Shard::chunk_guild`], and can be used to determine
305    /// how many members have not yet been received.
306    ///
307    /// ```rust,no_run
308    /// # use serenity::model::prelude::*;
309    /// # use serenity::prelude::*;
310    /// struct Handler;
311    ///
312    /// #[serenity::async_trait]
313    /// # #[cfg(feature = "client")]
314    /// impl EventHandler for Handler {
315    ///     async fn cache_ready(&self, ctx: Context, _: Vec<GuildId>) {
316    ///         println!("{} unknown members", ctx.cache.unknown_members());
317    ///     }
318    /// }
319    /// ```
320    ///
321    /// [`Shard::chunk_guild`]: crate::gateway::Shard::chunk_guild
322    pub fn unknown_members(&self) -> u64 {
323        let mut total = 0;
324
325        for guild_entry in self.guilds.iter() {
326            let guild = guild_entry.value();
327
328            let members = guild.members.len() as u64;
329
330            if guild.member_count > members {
331                total += guild.member_count - members;
332            }
333        }
334
335        total
336    }
337
338    /// Fetches a vector of all [`Guild`]s' Ids that are stored in the cache.
339    ///
340    /// Note that if you are utilizing multiple [`Shard`]s, then the guilds retrieved over all
341    /// shards are included in this count -- not just the current [`Context`]'s shard, if accessing
342    /// from one.
343    ///
344    /// # Examples
345    ///
346    /// Print all of the Ids of guilds in the Cache:
347    ///
348    /// ```rust,no_run
349    /// # use serenity::model::prelude::*;
350    /// # use serenity::prelude::*;
351    /// #
352    /// struct Handler;
353    ///
354    /// #[serenity::async_trait]
355    /// impl EventHandler for Handler {
356    ///     async fn ready(&self, context: Context, _: Ready) {
357    ///         let guilds = context.cache.guilds().len();
358    ///
359    ///         println!("Guilds in the Cache: {}", guilds);
360    ///     }
361    /// }
362    /// ```
363    ///
364    /// [`Context`]: crate::client::Context
365    /// [`Shard`]: crate::gateway::Shard
366    pub fn guilds(&self) -> Vec<GuildId> {
367        let unavailable_guilds = self.unavailable_guilds();
368
369        let unavailable_guild_ids = unavailable_guilds.iter().map(|i| *i.key());
370
371        self.guilds.iter().map(|i| *i.key()).chain(unavailable_guild_ids).collect()
372    }
373
374    /// Retrieves a [`GuildChannel`] from the cache based on the given Id.
375    #[inline]
376    #[deprecated = "Use Cache::guild and Guild::channels instead"]
377    pub fn channel<C: Into<ChannelId>>(&self, id: C) -> Option<GuildChannelRef<'_>> {
378        self.channel_(id.into())
379    }
380
381    fn channel_(&self, id: ChannelId) -> Option<GuildChannelRef<'_>> {
382        let guild_id = *self.channels.get(&id)?;
383        let guild_ref = self.guilds.get(&guild_id)?;
384        let channel = guild_ref.try_map(|g| g.channels.get(&id)).ok();
385        if let Some(channel) = channel {
386            return Some(CacheRef::from_mapped_ref(channel));
387        }
388
389        #[cfg(feature = "temp_cache")]
390        {
391            if let Some(channel) = self.temp_channels.get(&id) {
392                return Some(CacheRef::from_arc(channel));
393            }
394        }
395
396        None
397    }
398
399    /// Get a reference to the cached messages for a channel based on the given `Id`.
400    ///
401    /// # Examples
402    ///
403    /// Find all messages by user ID 8 in channel ID 7:
404    ///
405    /// ```rust,no_run
406    /// # let cache: serenity::cache::Cache = todo!();
407    /// let messages_in_channel = cache.channel_messages(7);
408    /// let messages_by_user = messages_in_channel
409    ///     .as_ref()
410    ///     .map(|msgs| msgs.values().filter(|m| m.author.id == 8).collect::<Vec<_>>());
411    /// ```
412    pub fn channel_messages(
413        &self,
414        channel_id: impl Into<ChannelId>,
415    ) -> Option<ChannelMessagesRef<'_>> {
416        self.messages.get(&channel_id.into()).map(CacheRef::from_ref)
417    }
418
419    /// Gets a reference to a guild from the cache based on the given `id`.
420    ///
421    /// # Examples
422    ///
423    /// Retrieve a guild from the cache and print its name:
424    ///
425    /// ```rust,no_run
426    /// # use serenity::cache::Cache;
427    /// #
428    /// # let cache = Cache::default();
429    /// // assuming the cache is in scope, e.g. via `Context`
430    /// if let Some(guild) = cache.guild(7) {
431    ///     println!("Guild name: {}", guild.name);
432    /// };
433    /// ```
434    #[inline]
435    pub fn guild<G: Into<GuildId>>(&self, id: G) -> Option<GuildRef<'_>> {
436        self.guild_(id.into())
437    }
438
439    fn guild_(&self, id: GuildId) -> Option<GuildRef<'_>> {
440        self.guilds.get(&id).map(CacheRef::from_ref)
441    }
442
443    /// Returns the number of cached guilds.
444    pub fn guild_count(&self) -> usize {
445        self.guilds.len()
446    }
447
448    /// Retrieves a [`Guild`]'s member from the cache based on the guild's and user's given Ids.
449    ///
450    /// # Examples
451    ///
452    /// Retrieving the member object of the user that posted a message, in a
453    /// [`EventHandler::message`] context:
454    ///
455    /// ```rust,no_run
456    /// # use serenity::cache::Cache;
457    /// # use serenity::http::Http;
458    /// # use serenity::model::channel::Message;
459    /// #
460    /// # async fn run(http: Http, cache: Cache, message: Message) {
461    /// #
462    /// let roles_len = {
463    ///     let channel = match cache.channel(message.channel_id) {
464    ///         Some(channel) => channel,
465    ///         None => {
466    ///             if let Err(why) = message.channel_id.say(http, "Error finding channel data").await {
467    ///                 println!("Error sending message: {:?}", why);
468    ///             }
469    ///             return;
470    ///         },
471    ///     };
472    ///
473    ///     cache.member(channel.guild_id, message.author.id).map(|m| m.roles.len())
474    /// };
475    ///
476    /// let message_res = if let Some(roles_len) = roles_len {
477    ///     let msg = format!("You have {} roles", roles_len);
478    ///     message.channel_id.say(&http, &msg).await
479    /// } else {
480    ///     message.channel_id.say(&http, "Error finding member data").await
481    /// };
482    ///
483    /// if let Err(why) = message_res {
484    ///     println!("Error sending message: {:?}", why);
485    /// }
486    /// # }
487    /// ```
488    ///
489    /// [`EventHandler::message`]: crate::client::EventHandler::message
490    /// [`members`]: crate::model::guild::Guild::members
491    #[inline]
492    #[deprecated = "Use Cache::guild and Guild::members instead"]
493    pub fn member(
494        &self,
495        guild_id: impl Into<GuildId>,
496        user_id: impl Into<UserId>,
497    ) -> Option<MemberRef<'_>> {
498        self.member_(guild_id.into(), user_id.into())
499    }
500
501    fn member_(&self, guild_id: GuildId, user_id: UserId) -> Option<MemberRef<'_>> {
502        let member = self.guilds.get(&guild_id)?.try_map(|g| g.members.get(&user_id)).ok()?;
503        Some(CacheRef::from_mapped_ref(member))
504    }
505
506    #[inline]
507    #[deprecated = "Use Cache::guild and Guild::roles instead"]
508    pub fn guild_roles(&self, guild_id: impl Into<GuildId>) -> Option<GuildRolesRef<'_>> {
509        self.guild_roles_(guild_id.into())
510    }
511
512    fn guild_roles_(&self, guild_id: GuildId) -> Option<GuildRolesRef<'_>> {
513        let roles = self.guilds.get(&guild_id)?.map(|g| &g.roles);
514        Some(CacheRef::from_mapped_ref(roles))
515    }
516
517    /// This method clones and returns all unavailable guilds.
518    #[inline]
519    pub fn unavailable_guilds(&self) -> ReadOnlyMapRef<'_, GuildId, ()> {
520        self.unavailable_guilds.as_read_only()
521    }
522
523    /// This method returns all channels from a guild of with the given `guild_id`.
524    #[inline]
525    #[deprecated = "Use Cache::guild and Guild::channels instead"]
526    pub fn guild_channels(&self, guild_id: impl Into<GuildId>) -> Option<GuildChannelsRef<'_>> {
527        self.guild_channels_(guild_id.into())
528    }
529
530    fn guild_channels_(&self, guild_id: GuildId) -> Option<GuildChannelsRef<'_>> {
531        let channels = self.guilds.get(&guild_id)?.map(|g| &g.channels);
532        Some(CacheRef::from_mapped_ref(channels))
533    }
534
535    /// Returns the number of guild channels in the cache.
536    pub fn guild_channel_count(&self) -> usize {
537        self.channels.len()
538    }
539
540    /// Returns the number of shards.
541    #[inline]
542    pub fn shard_count(&self) -> u32 {
543        self.shard_data.read().total
544    }
545
546    /// Retrieves a [`Channel`]'s message from the cache based on the channel's and message's given
547    /// Ids.
548    ///
549    /// **Note**: This will clone the entire message.
550    ///
551    /// # Examples
552    ///
553    /// Retrieving the message object from a channel, in a [`EventHandler::message`] context:
554    ///
555    /// ```rust,no_run
556    /// # use serenity::cache::Cache;
557    /// # use serenity::model::channel::Message;
558    /// #
559    /// # fn run(cache: Cache, message: Message) {
560    /// #
561    /// match cache.message(message.channel_id, message.id) {
562    ///     Some(m) => assert_eq!(message.content, m.content),
563    ///     None => println!("No message found in cache."),
564    /// };
565    /// # }
566    /// ```
567    ///
568    /// [`EventHandler::message`]: crate::client::EventHandler::message
569    #[inline]
570    pub fn message<C, M>(&self, channel_id: C, message_id: M) -> Option<MessageRef<'_>>
571    where
572        C: Into<ChannelId>,
573        M: Into<MessageId>,
574    {
575        self.message_(channel_id.into(), message_id.into())
576    }
577
578    fn message_(&self, channel_id: ChannelId, message_id: MessageId) -> Option<MessageRef<'_>> {
579        #[cfg(feature = "temp_cache")]
580        if let Some(message) = self.temp_messages.get(&message_id) {
581            return Some(CacheRef::from_arc(message));
582        }
583
584        let channel_messages = self.messages.get(&channel_id)?;
585        let message = channel_messages.try_map(|messages| messages.get(&message_id)).ok()?;
586        Some(CacheRef::from_mapped_ref(message))
587    }
588
589    /// Retrieves a [`Guild`]'s role by their Ids.
590    ///
591    /// **Note**: This will clone the entire role. Instead, retrieve the guild and retrieve from
592    /// the guild's [`roles`] map to avoid this.
593    ///
594    /// # Examples
595    ///
596    /// Retrieve a role from the cache and print its name:
597    ///
598    /// ```rust,no_run
599    /// # use serenity::cache::Cache;
600    /// #
601    /// # let cache = Cache::default();
602    /// // assuming the cache is in scope, e.g. via `Context`
603    /// if let Some(role) = cache.role(7, 77) {
604    ///     println!("Role with Id 77 is called {}", role.name);
605    /// };
606    /// ```
607    ///
608    /// [`Guild`]: crate::model::guild::Guild
609    /// [`roles`]: crate::model::guild::Guild::roles
610    #[inline]
611    #[deprecated = "Use Cache::guild and Guild::roles instead"]
612    pub fn role<G, R>(&self, guild_id: G, role_id: R) -> Option<GuildRoleRef<'_>>
613    where
614        G: Into<GuildId>,
615        R: Into<RoleId>,
616    {
617        self.role_(guild_id.into(), role_id.into())
618    }
619
620    fn role_(&self, guild_id: GuildId, role_id: RoleId) -> Option<GuildRoleRef<'_>> {
621        let role = self.guilds.get(&guild_id)?.try_map(|g| g.roles.get(&role_id)).ok()?;
622        Some(CacheRef::from_mapped_ref(role))
623    }
624
625    /// Returns the settings.
626    ///
627    /// # Examples
628    ///
629    /// Printing the maximum number of messages in a channel to be cached:
630    ///
631    /// ```rust
632    /// use serenity::cache::Cache;
633    ///
634    /// # fn test() {
635    /// let mut cache = Cache::new();
636    /// println!("Max settings: {}", cache.settings().max_messages);
637    /// # }
638    /// ```
639    pub fn settings(&self) -> SettingsRef<'_> {
640        CacheRef::from_guard(self.settings.read())
641    }
642
643    /// Sets the maximum amount of messages per channel to cache.
644    ///
645    /// By default, no messages will be cached.
646    pub fn set_max_messages(&self, max: usize) {
647        self.settings.write().max_messages = max;
648    }
649
650    /// Retrieves a [`User`] from the cache's [`Self::users`] map, if it exists.
651    ///
652    /// The only advantage of this method is that you can pass in anything that is indirectly a
653    /// [`UserId`].
654    ///
655    /// # Examples
656    ///
657    /// Retrieve a user from the cache and print their name:
658    ///
659    /// ```rust,no_run
660    /// # use serenity::client::Context;
661    /// #
662    /// # async fn test(context: &Context) -> Result<(), Box<dyn std::error::Error>> {
663    /// if let Some(user) = context.cache.user(7) {
664    ///     println!("User with Id 7 is currently named {}", user.name);
665    /// }
666    /// # Ok(())
667    /// # }
668    /// ```
669    #[inline]
670    pub fn user<U: Into<UserId>>(&self, user_id: U) -> Option<UserRef<'_>> {
671        self.user_(user_id.into())
672    }
673
674    #[cfg(feature = "temp_cache")]
675    fn user_(&self, user_id: UserId) -> Option<UserRef<'_>> {
676        if let Some(user) = self.users.get(&user_id) {
677            Some(CacheRef::from_ref(user))
678        } else {
679            self.temp_users.get(&user_id).map(CacheRef::from_arc)
680        }
681    }
682
683    #[cfg(not(feature = "temp_cache"))]
684    fn user_(&self, user_id: UserId) -> Option<UserRef<'_>> {
685        self.users.get(&user_id).map(CacheRef::from_ref)
686    }
687
688    /// Clones all users and returns them.
689    #[inline]
690    pub fn users(&self) -> ReadOnlyMapRef<'_, UserId, User> {
691        self.users.as_read_only()
692    }
693
694    /// Returns the amount of cached users.
695    #[inline]
696    pub fn user_count(&self) -> usize {
697        self.users.len()
698    }
699
700    /// This method provides a reference to the user used by the bot.
701    #[inline]
702    pub fn current_user(&self) -> CurrentUserRef<'_> {
703        CacheRef::from_guard(self.user.read())
704    }
705
706    /// Returns a channel category matching the given ID
707    #[deprecated = "Use Cache::guild, Guild::channels, and GuildChannel::kind"]
708    pub fn category(&self, channel_id: ChannelId) -> Option<GuildChannelRef<'_>> {
709        #[allow(deprecated)]
710        let channel = self.channel(channel_id)?;
711        if channel.kind == ChannelType::Category {
712            Some(channel)
713        } else {
714            None
715        }
716    }
717
718    /// Returns the parent category of the given channel ID.
719    #[deprecated = "Use Cache::guild, Guild::channels, and GuildChannel::parent_id"]
720    pub fn channel_category_id(&self, channel_id: ChannelId) -> Option<ChannelId> {
721        #[allow(deprecated)]
722        self.channel(channel_id)?.parent_id
723    }
724
725    /// Clones all channel categories in the given guild and returns them.
726    pub fn guild_categories(&self, guild_id: GuildId) -> Option<HashMap<ChannelId, GuildChannel>> {
727        let guild = self.guilds.get(&guild_id)?;
728        Some(
729            guild
730                .channels
731                .iter()
732                .filter(|(_id, channel)| channel.kind == ChannelType::Category)
733                .map(|(id, channel)| (*id, channel.clone()))
734                .collect(),
735        )
736    }
737
738    /// Updates the cache with the update implementation for an event or other custom update
739    /// implementation.
740    ///
741    /// Refer to the documentation for [`CacheUpdate`] for more information.
742    ///
743    /// # Examples
744    ///
745    /// Refer to the [`CacheUpdate` examples].
746    ///
747    /// [`CacheUpdate` examples]: CacheUpdate#examples
748    #[instrument(skip(self, e))]
749    pub fn update<E: CacheUpdate>(&self, e: &mut E) -> Option<E::Output> {
750        e.update(self)
751    }
752
753    pub(crate) fn update_user_entry(&self, user: &User) {
754        if let Some(users) = &self.users.0 {
755            match users.entry(user.id) {
756                Entry::Vacant(e) => {
757                    e.insert(user.clone());
758                },
759                Entry::Occupied(mut e) => {
760                    e.get_mut().clone_from(user);
761                },
762            }
763        }
764    }
765}
766
767impl Default for Cache {
768    fn default() -> Self {
769        Self::new_with_settings(Settings::default())
770    }
771}
772
773#[cfg(test)]
774mod test {
775
776    use crate::cache::{Cache, CacheUpdate, Settings};
777    use crate::model::prelude::*;
778
779    #[test]
780    fn test_cache_messages() {
781        let settings = Settings {
782            max_messages: 2,
783            ..Default::default()
784        };
785        let cache = Cache::new_with_settings(settings);
786
787        // Test inserting one message into a channel's message cache.
788        let mut event = MessageCreateEvent {
789            message: Message {
790                id: MessageId::new(3),
791                guild_id: Some(GuildId::new(1)),
792                ..Default::default()
793            },
794        };
795
796        // Check that the channel cache doesn't exist.
797        assert!(!cache.messages.contains_key(&event.message.channel_id));
798        // Add first message, none because message ID 2 doesn't already exist.
799        assert!(event.update(&cache).is_none());
800        // None, it only returns the oldest message if the cache was already full.
801        assert!(event.update(&cache).is_none());
802        // Assert there's only 1 message in the channel's message cache.
803        assert_eq!(cache.messages.get(&event.message.channel_id).unwrap().len(), 1);
804
805        // Add a second message, assert that channel message cache length is 2.
806        event.message.id = MessageId::new(4);
807        assert!(event.update(&cache).is_none());
808        assert_eq!(cache.messages.get(&event.message.channel_id).unwrap().len(), 2);
809
810        // Add a third message, the first should now be removed.
811        event.message.id = MessageId::new(5);
812        assert!(event.update(&cache).is_some());
813
814        {
815            let channel = cache.messages.get(&event.message.channel_id).unwrap();
816
817            assert_eq!(channel.len(), 2);
818            // Check that the first message is now removed.
819            assert!(!channel.contains_key(&MessageId::new(3)));
820        }
821
822        let channel = GuildChannel {
823            id: event.message.channel_id,
824            guild_id: event.message.guild_id.unwrap(),
825            ..Default::default()
826        };
827
828        // Add a channel delete event to the cache, the cached messages for that channel should now
829        // be gone.
830        let mut delete = ChannelDeleteEvent {
831            channel: channel.clone(),
832        };
833        assert!(cache.update(&mut delete).is_some());
834        assert!(!cache.messages.contains_key(&delete.channel.id));
835
836        // Test deletion of a guild channel's message cache when a GuildDeleteEvent is received.
837        let mut guild_create = GuildCreateEvent {
838            guild: Guild {
839                id: GuildId::new(1),
840                channels: HashMap::from([(ChannelId::new(2), channel)]),
841                ..Default::default()
842            },
843        };
844        assert!(cache.update(&mut guild_create).is_none());
845        assert!(cache.update(&mut event).is_none());
846
847        let mut guild_delete = GuildDeleteEvent {
848            guild: UnavailableGuild {
849                id: GuildId::new(1),
850                unavailable: false,
851            },
852        };
853
854        // The guild existed in the cache, so the cache's guild is returned by the update.
855        assert!(cache.update(&mut guild_delete).is_some());
856
857        // Assert that the channel's message cache no longer exists.
858        assert!(!cache.messages.contains_key(&ChannelId::new(2)));
859    }
860}