aboutsummaryrefslogtreecommitdiff
path: root/src/ext/cache
diff options
context:
space:
mode:
authorAustin Hellyer <[email protected]>2016-11-26 11:37:18 -0800
committerAustin Hellyer <[email protected]>2016-11-26 11:37:18 -0800
commit77354ab321bec1ff66af0e27eb87a7eec3e3db24 (patch)
tree693b43ae7be07be11426faf6e6282d838e426a04 /src/ext/cache
parentMake Cache::get_channel return a reference (diff)
downloadserenity-77354ab321bec1ff66af0e27eb87a7eec3e3db24.tar.xz
serenity-77354ab321bec1ff66af0e27eb87a7eec3e3db24.zip
Add a bit more docs
Diffstat (limited to 'src/ext/cache')
-rw-r--r--src/ext/cache/mod.rs289
1 files changed, 283 insertions, 6 deletions
diff --git a/src/ext/cache/mod.rs b/src/ext/cache/mod.rs
index a8d62c9..923c55a 100644
--- a/src/ext/cache/mod.rs
+++ b/src/ext/cache/mod.rs
@@ -1,3 +1,81 @@
+//! A cache of events received over a [`Shard`], where storing at least some
+//! data from the event is possible.
+//!
+//! This acts as a hot cache, to avoid making requests over the REST API through
+//! the [`http`] module where possible. All fields are public, and do not have
+//! getters, to allow you more flexibility with the stored data. However, this
+//! allows data to be "corrupted", and _may or may not_ cause misfunctions
+//! within the library. Mutate data at your own discretion.
+//!
+//! A "globally available" instance of the Cache is available at
+//! [`client::CACHE`]. This is the instance that is updated by the library,
+//! meaning you should _not_ need to maintain updating it yourself in any case.
+//!
+//! # Use by the Context
+//!
+//! The [`Context`] will automatically attempt to pull from the cache for you.
+//! For example, the [`Context::get_channel`] method will attempt to find the
+//! channel in the cache. If it can not find it, it will perform a request
+//! through the REST API, and then insert a clone of the channel - if found -
+//! into the Cache, giving you the original.
+//!
+//! This allows you to save a step, by only needing to perform the
+//! [`Context::get_channel`] call and not need to first search through the cache
+//! - and if not found - _then_ perform an HTTP request through the Context or
+//! [`http`] module.
+//!
+//! Additionally, note that some information received through events can _not_
+//! be retrieved through the REST API. This is information such as [`Role`]s in
+//! [`LiveGuild`]s.
+//!
+//! # Use by Models
+//!
+//! Most models of Discord objects, such as the [`Message`], [`PublicChannel`],
+//! or [`Emoji`], have methods for interacting with that single instance. This
+//! feature is only compiled if the `methods` feature is enabled. An example of
+//! this is [`LiveGuild::edit`], which performs a check to ensure that the
+//! current user is the owner of the guild, prior to actually performing the
+//! HTTP request. The cache is involved due to the function's use of unlocking
+//! the cache and retrieving the Id of the current user, and comparing it to
+//! the Id of the user that owns the guild. This is an inexpensive method of
+//! being able to access data required by these sugary methods.
+//!
+//! # Do I need the Cache?
+//!
+//! If you're asking this, the answer is likely "definitely yes" or
+//! "definitely no"; any in-between tends to be "yes". If you are low on RAM,
+//! and need to run on only a couple MB, then the answer is "definitely no". If
+//! you do not care about RAM and want your bot to be able to access data
+//! while needing to hit the REST API as little as possible, then the answer
+//! is "yes".
+//!
+//! # Special cases in the Cache
+//!
+//! Some items in the cache, notably [`Call`]s and [`Group`]s, will "always be
+//! empty". The exception to this rule, is for:
+//!
+//! 1. Bots which used to be userbots prior to the conversion made available by
+//! Discord when the official Bot API was introduced;
+//! 2. For groups and calls:
+//! 2a. Bots that have friends from before the conversion that have not been
+//! removed, as those users can still add the bots to groups;
+//! 2b. Bots that have the "Create Group" endpoint whitelisted specifically for
+//! them.
+//!
+//! [`Call`]: ../../model/struct.Call.html
+//! [`Context`]: ../../client/struct.Context.html
+//! [`Context::get_channel`]: ../../client/struct.Context.html#method.get_channel
+//! [`Emoji`]: ../../model/struct.Emoji.html
+//! [`Group`]: ../../model/struct.Group.html
+//! [`LiveGuild`]: ../../model/struct.LiveGuild.html
+//! [`LiveGuild::edit`]: ../../model/struct.LiveGuild.html#method.edit
+//! [`Message`]: ../../model/struct.Message.html
+//! [`PublicChannel`]: ../../model/struct.PublicChannel.html
+//! [`Role`]: ../../model/struct.Role.html
+//! [`Shard`]: ../../client/gateway/struct.Shard.html
+//! [`client::CACHE`]: ../../client/struct.CACHE.html
+//! [`http`]: ../../client/http/index.html
+
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::default::Default;
@@ -42,31 +120,86 @@ pub struct Cache {
/// where the key is the Id of the [`PrivateChannel`] or [`Group`] hosting
/// the call.
///
- /// For bot users this will almost always be empty.
+ /// For bot users this will always be empty, except for in [special cases].
+ ///
+ /// [`Group`]: ../../model/struct.Group.html
+ /// [`PrivateChannel`]: ../../model/struct.PrivateChannel.html
+ /// [special cases]: index.html#special-cases-in-the-cache
pub calls: HashMap<ChannelId, Call>,
/// A map of the groups that the current user is in.
///
- /// For bot users this will almost always be empty.
+ /// For bot users this will always be empty, except for in [special cases].
+ ///
+ /// [special cases]: index.html#special-cases-in-the-cache
pub groups: HashMap<ChannelId, Group>,
/// Settings specific to a guild.
///
- /// This will always be empty for bot accounts.
+ /// This will always be empty for bot users.
pub guild_settings: HashMap<Option<GuildId>, UserGuildSettings>,
+ /// A map of guilds with full data available. This includes data like
+ /// [`Role`]s and [`Emoji`]s that are not available through the REST API.
+ ///
+ /// [`Emoji`]: ../../model/struct.Emoji.html
+ /// [`Role`]: ../../model/struct.Role.html
pub guilds: HashMap<GuildId, Guild>,
- /// A list of notes that a user has made for individual users.
+ /// A map of notes that a user has made for individual users.
+ ///
+ /// An empty note is equivilant to having no note, and creating an empty
+ /// note is equivilant to deleting a note.
///
- /// This will always be empty for bot accounts.
+ /// This will always be empty for bot users.
pub notes: HashMap<UserId, String>,
+ /// A map of users' presences. This is updated in real-time. Note that
+ /// status updates are often "eaten" by the gateway, and this should not
+ /// be treated as being entirely 100% accurate.
pub presences: HashMap<UserId, Presence>,
+ /// A map of direct message channels that the current user has open with
+ /// other users.
pub private_channels: HashMap<ChannelId, PrivateChannel>,
+ /// A map of relationships that the current user has with other users.
+ ///
+ /// For bot users this will always be empty, except for in [special cases].
+ ///
+ /// [special cases]: index.html#special-cases-in-the-cache
pub relationships: HashMap<UserId, Relationship>,
/// Account-specific settings for a user account.
pub settings: Option<UserSettings>,
+ /// A list of guilds which are "unavailable". Refer to the documentation for
+ /// [`Event::GuildUnavailable`] for more information on when this can occur.
+ ///
+ /// Additionally, guilds are always unavailable for bot users when a Ready
+ /// is received. Guilds are "sent in" over time through the receiving of
+ /// [`Event::GuildCreate`]s.
+ ///
+ /// [`Event::GuildCreate`]: ../../model/enum.Event.html#variant.GuildCreate
+ /// [`Event::GuildUnavailable`]: ../../model/enum.Event.html#variant.GuildUnavailable
pub unavailable_guilds: Vec<GuildId>,
+ /// The current user "logged in" and for which events are being received
+ /// for.
+ ///
+ /// The current user contains information that a regular [`User`] does not,
+ /// such as whether it is a bot, whether the user is verified, etc.
+ ///
+ /// Refer to the documentation for [`CurrentUser`] for more information.
+ ///
+ /// [`CurrentUser`]: ../../model/struct.CurrentUser.html
+ /// [`User`]: ../../model/struct.User.html
pub user: CurrentUser,
}
impl Cache {
+ /// Calculates the number of [`Member`]s that have not had data received.
+ ///
+ /// The important detail to note here is that this is the number of
+ /// _member_s that have not had data downloaded. A single [`User`] may have
+ /// multiple associated member objects that have not been received.
+ ///
+ /// This can be used in combination with [`Shard::sync_guilds`], and can be
+ /// used to determine how many members have not yet been downloaded.
+ ///
+ /// [`Member`]: ../../model/struct.Member.html
+ /// [`Shard::sync_guilds`]: ../../client/gateway/struct.Shard.html#method.sync_guilds
+ /// [`User`]: ../../model/struct.User.html
pub fn unknown_members(&self) -> u64 {
let mut total = 0;
@@ -81,6 +214,16 @@ impl Cache {
total
}
+ /// Calculates a vector of all [`PrivateChannel`] and [`Group`] Ids that are
+ /// stored in the cache.
+ ///
+ /// # Examples
+ ///
+ /// If there are 6 private channels and 2 groups in the cache, then `8` Ids
+ /// will be returned.
+ ///
+ /// [`Group`]: ../../model/struct.Group.html
+ /// [`PrivateChannel`]: ../../model/struct.PrivateChannel.html
pub fn all_private_channels(&self) -> Vec<ChannelId> {
self.groups
.keys()
@@ -89,6 +232,15 @@ impl Cache {
.collect()
}
+ /// Calculates a vector of all [`Guild`]s' Ids that are stored in the cache.
+ ///
+ /// Note that if you are utilizing multiple [`Shard`]s, then the guilds
+ /// retrieved over all shards are included in this count -- not just the
+ /// current [`Context`]'s shard, if accessing from one.
+ ///
+ /// [`Context`]: ../../client/struct.Context.html
+ /// [`Guild`]: ../../model/struct.Guild.html
+ /// [`Shard`]: ../../client/gateway/struct.Shard.html
pub fn all_guilds(&self) -> Vec<GuildId> {
self.guilds
.values()
@@ -110,10 +262,25 @@ impl Cache {
.collect::<Vec<_>>()
}
+ /// Retrieves a reference to a [`Call`] from the cache based on the
+ /// associated [`Group`]'s channel Id.
+ ///
+ /// [`Call`]: ../../model/struct.Call.html
+ /// [`Group`]: ../../model/struct.Group.html
pub fn get_call<C: Into<ChannelId>>(&self, group_id: C) -> Option<&Call> {
self.calls.get(&group_id.into())
}
+ /// Retrieves a [`Channel`] from the cache based on the given Id.
+ ///
+ /// This will search the [`groups`] map, the [`private_channels`] map, and
+ /// then the map of [`guilds`] to find the channel.
+ ///
+ /// [`Channel`]: ../../model/enum.Channel.html
+ /// [`Guild`]: ../../model/struct.Guild.html
+ /// [`groups`]: #structfield.groups
+ /// [`private_channels`]: #structfield.private_channels
+ /// [`guilds`]: #structfield.guilds
pub fn get_channel<C: Into<ChannelId>>(&self, id: C) -> Option<ChannelRef> {
let id = id.into();
@@ -136,10 +303,38 @@ impl Cache {
None
}
+ /// Retrieves a reference to a guild from the cache based on the given Id.
pub fn get_guild<G: Into<GuildId>>(&self, id: G) -> Option<&Guild> {
self.guilds.get(&id.into())
}
+ /// Retrieves a reference to a [`Guild`]'s channel. Unlike [`get_channel`],
+ /// this will only search guilds for the given channel.
+ ///
+ /// # Examples
+ ///
+ /// Getting a guild's channel via the Id of the message received through a
+ /// [`Client::on_message`] event dispatch:
+ ///
+ /// ```rust,ignore
+ /// use serenity::cache::CACHE;
+ ///
+ /// let cache = CACHE.read().unwrap();
+ ///
+ /// let channel = match cache.get_guild_channel(message.channel_id) {
+ /// Some(channel) => channel,
+ /// None => {
+ /// context.say("Could not find guild's channel data")
+ /// .map_err(|why| println!("Err sending message: {:?}", why));
+ ///
+ /// return;
+ /// },
+ /// };
+ /// ```
+ ///
+ /// [`Client::on_message`]: ../../client/struct.Client.html#method.on_message
+ /// [`Guild`]: ../../model/struct.Guild.html
+ /// [`get_channel`]: #method.get_channel
pub fn get_guild_channel<C: Into<ChannelId>>(&self, id: C) -> Option<&GuildChannel> {
let id = id.into();
@@ -152,10 +347,57 @@ impl Cache {
None
}
+ /// Retrieves a reference to a [`Group`] from the cache based on the given
+ /// associated channel Id.
+ ///
+ /// [`Group`]: ../../model/struct.Group.html
pub fn get_group<C: Into<ChannelId>>(&self, id: C) -> Option<&Group> {
self.groups.get(&id.into())
}
+ /// Retrieves a reference to a [`Guild`]'s member from the cache based on
+ /// the guild's and user's given Ids.
+ ///
+ /// # Examples
+ ///
+ /// Retrieving the member object of the user that posted a message, in a
+ /// [`Client::on_message`] context:
+ ///
+ /// ```rust,ignore
+ /// use serenity::client::CACHE;
+ ///
+ /// // assuming you are in a context
+ ///
+ /// let cache = CACHE.read().unwrap();
+ /// let member = {
+ /// let channel = match cache.get_guild_channel(message.channel_id) {
+ /// Some(channel) => channel,
+ /// None => {
+ /// if let Err(why) = context.say("Err finding channel data") {
+ /// println!("Err sending message: {:?}", why);
+ /// }
+ /// },
+ /// };
+ ///
+ /// match cache.get_member(channel.guild_id, message.author.id) {
+ /// Some(member) => member,
+ /// None => {
+ /// if let Err(why) = context.say("Err finding member data") {
+ /// println!("Err sending message: {:?}", why);
+ /// }
+ /// },
+ /// }
+ /// };
+ ///
+ /// let msg = format!("You have {} roles", member.roles.len());
+ ///
+ /// if let Err(why) = context.say(&msg) {
+ /// println!("Err sending message: {:?}", why);
+ /// }
+ /// ```
+ ///
+ /// [`Client::on_message`]: ../../client/struct.Client.html#method.on_message
+ /// [`Guild`]: ../../model/struct.Guild.html
pub fn get_member<G, U>(&self, guild_id: G, user_id: U) -> Option<&Member>
where G: Into<GuildId>, U: Into<UserId> {
self.guilds
@@ -168,6 +410,9 @@ impl Cache {
})
}
+ /// Retrieves a reference to a [`Guild`]'s role by their Ids.
+ ///
+ /// [`Guild`]: ../../model/struct.Guild.html
pub fn get_role<G, R>(&self, guild_id: G, role_id: R) -> Option<&Role>
where G: Into<GuildId>, R: Into<RoleId> {
if let Some(guild) = self.guilds.get(&guild_id.into()) {
@@ -180,6 +425,7 @@ impl Cache {
/// Update the cache according to the changes described in the given event.
#[allow(cyclomatic_complexity)]
#[allow(unneeded_field_pattern)]
+ #[doc(hidden)]
pub fn update(&mut self, event: &Event) {
match *event {
Event::CallCreate(ref event) => {
@@ -300,6 +546,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_call_create(&mut self, event: &CallCreateEvent) {
match self.calls.entry(event.call.channel_id) {
Entry::Vacant(e) => {
@@ -311,11 +558,13 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_call_delete(&mut self, event: &CallDeleteEvent)
-> Option<Call> {
self.calls.remove(&event.channel_id)
}
+ #[doc(hidden)]
pub fn update_with_call_update(&mut self, event: &CallUpdateEvent, old: bool)
-> Option<Call> {
let item = if old {
@@ -334,6 +583,7 @@ impl Cache {
item
}
+ #[doc(hidden)]
pub fn update_with_channel_create(&mut self, event: &ChannelCreateEvent)
-> Option<Channel> {
match event.channel {
@@ -364,6 +614,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_channel_delete(&mut self, event: &ChannelDeleteEvent)
-> Option<Channel> {
match event.channel {
@@ -387,6 +638,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_channel_pins_update(&mut self,
event: &ChannelPinsUpdateEvent) {
if let Some(channel) = self.private_channels.get_mut(&event.channel_id) {
@@ -414,6 +666,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_channel_recipient_add(&mut self,
event: &ChannelRecipientAddEvent) {
self.groups
@@ -422,6 +675,7 @@ impl Cache {
event.user.clone()));
}
+ #[doc(hidden)]
pub fn update_with_channel_recipient_remove(&mut self,
event: &ChannelRecipientRemoveEvent) {
self.groups
@@ -429,6 +683,7 @@ impl Cache {
.map(|group| group.recipients.remove(&event.user.id));
}
+ #[doc(hidden)]
pub fn update_with_channel_update(&mut self, event: &ChannelUpdateEvent) {
match event.channel {
Channel::Group(ref group) => {
@@ -465,15 +720,18 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_guild_create(&mut self, event: &GuildCreateEvent) {
self.guilds.insert(event.guild.id, event.guild.clone());
}
+ #[doc(hidden)]
pub fn update_with_guild_delete(&mut self, event: &GuildDeleteEvent)
-> Option<Guild> {
self.guilds.remove(&event.guild.id)
}
+ #[doc(hidden)]
pub fn update_with_guild_emojis_update(&mut self,
event: &GuildEmojisUpdateEvent) {
self.guilds
@@ -481,6 +739,7 @@ impl Cache {
.map(|guild| guild.emojis.extend(event.emojis.clone()));
}
+ #[doc(hidden)]
pub fn update_with_guild_member_add(&mut self,
event: &GuildMemberAddEvent) {
self.guilds
@@ -492,6 +751,7 @@ impl Cache {
});
}
+ #[doc(hidden)]
pub fn update_with_guild_member_remove(&mut self,
event: &GuildMemberRemoveEvent)
-> Option<Member> {
@@ -508,6 +768,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_guild_member_update(&mut self,
event: &GuildMemberUpdateEvent)
-> Option<Member> {
@@ -545,6 +806,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_guild_members_chunk(&mut self,
event: &GuildMembersChunkEvent) {
self.guilds
@@ -552,6 +814,7 @@ impl Cache {
.map(|guild| guild.members.extend(event.members.clone()));
}
+ #[doc(hidden)]
pub fn update_with_guild_role_create(&mut self,
event: &GuildRoleCreateEvent) {
self.guilds
@@ -559,6 +822,7 @@ impl Cache {
.map(|guild| guild.roles.insert(event.role.id, event.role.clone()));
}
+ #[doc(hidden)]
pub fn update_with_guild_role_delete(&mut self,
event: &GuildRoleDeleteEvent)
-> Option<Role> {
@@ -572,6 +836,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_guild_role_update(&mut self,
event: &GuildRoleUpdateEvent)
-> Option<Role> {
@@ -587,6 +852,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_guild_sync(&mut self, event: &GuildSyncEvent) {
self.guilds
.get_mut(&event.guild_id)
@@ -597,6 +863,7 @@ impl Cache {
});
}
+ #[doc(hidden)]
pub fn update_with_guild_unavailable(&mut self,
event: &GuildUnavailableEvent) {
if !self.unavailable_guilds.contains(&event.guild_id) {
@@ -604,6 +871,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_guild_update(&mut self, event: &GuildUpdateEvent) {
self.guilds
.get_mut(&event.guild.id)
@@ -619,6 +887,7 @@ impl Cache {
});
}
+ #[doc(hidden)]
pub fn update_with_presences_replace(&mut self, event: &PresencesReplaceEvent) {
self.presences.clone_from(&{
let mut p = HashMap::default();
@@ -631,6 +900,7 @@ impl Cache {
});
}
+ #[doc(hidden)]
pub fn update_with_presence_update(&mut self, event: &PresenceUpdateEvent) {
if let Some(guild_id) = event.guild_id {
if let Some(guild) = self.guilds.get_mut(&guild_id) {
@@ -646,6 +916,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_ready(&mut self, ready: &ReadyEvent) {
let ready = ready.ready.clone();
@@ -695,16 +966,19 @@ impl Cache {
self.user = ready.user;
}
+ #[doc(hidden)]
pub fn update_with_relationship_add(&mut self, event: &RelationshipAddEvent) {
self.relationships.insert(event.relationship.id,
event.relationship.clone());
}
+ #[doc(hidden)]
pub fn update_with_relationship_remove(&mut self,
event: &RelationshipRemoveEvent) {
self.relationships.remove(&event.user_id);
}
+ #[doc(hidden)]
pub fn update_with_user_guild_settings_update(&mut self,
event: &UserGuildSettingsUpdateEvent)
-> Option<UserGuildSettings> {
@@ -713,6 +987,7 @@ impl Cache {
.map(|guild_setting| mem::replace(guild_setting, event.settings.clone()))
}
+ #[doc(hidden)]
pub fn update_with_user_note_update(&mut self,
event: &UserNoteUpdateEvent)
-> Option<String> {
@@ -723,6 +998,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_user_settings_update(&mut self,
event: &UserSettingsUpdateEvent,
old: bool)
@@ -751,11 +1027,13 @@ impl Cache {
item
}
+ #[doc(hidden)]
pub fn update_with_user_update(&mut self, event: &UserUpdateEvent)
-> CurrentUser {
mem::replace(&mut self.user, event.current_user.clone())
}
+ #[doc(hidden)]
pub fn update_with_voice_state_update(&mut self,
event: &VoiceStateUpdateEvent) {
if let Some(guild_id) = event.guild_id {
@@ -857,7 +1135,6 @@ fn update_presence(presences: &mut HashMap<UserId, Presence>,
}
/// A reference to a private channel, guild's channel, or group.
-#[derive(Clone, Copy, Debug)]
pub enum ChannelRef<'a> {
/// A group's channel
Group(&'a Group),