diff options
| author | Austin Hellyer <[email protected]> | 2016-11-26 11:37:18 -0800 |
|---|---|---|
| committer | Austin Hellyer <[email protected]> | 2016-11-26 11:37:18 -0800 |
| commit | 77354ab321bec1ff66af0e27eb87a7eec3e3db24 (patch) | |
| tree | 693b43ae7be07be11426faf6e6282d838e426a04 /src/ext/cache | |
| parent | Make Cache::get_channel return a reference (diff) | |
| download | serenity-77354ab321bec1ff66af0e27eb87a7eec3e3db24.tar.xz serenity-77354ab321bec1ff66af0e27eb87a7eec3e3db24.zip | |
Add a bit more docs
Diffstat (limited to 'src/ext/cache')
| -rw-r--r-- | src/ext/cache/mod.rs | 289 |
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), |