diff options
Diffstat (limited to 'src/ext/cache')
| -rw-r--r-- | src/ext/cache/mod.rs | 1023 |
1 files changed, 0 insertions, 1023 deletions
diff --git a/src/ext/cache/mod.rs b/src/ext/cache/mod.rs deleted file mode 100644 index f1326d4..0000000 --- a/src/ext/cache/mod.rs +++ /dev/null @@ -1,1023 +0,0 @@ -//! A cache of events received over a `Shard`, where storing at least some -//! data from the event is possible. -//! -//! This acts as a cache, to avoid making requests over the REST API through -//! the [`rest`] 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 -//! [`rest`] 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". -//! -//! [`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 -//! [`client::CACHE`]: ../../client/struct.CACHE.html -//! [`rest`]: ../../client/rest/index.html - -use std::collections::hash_map::Entry; -use std::collections::{HashMap, HashSet}; -use std::default::Default; -use std::sync::{Arc, RwLock}; -use std::mem; -use ::model::*; -use ::model::event::*; - -/// A cache of all events received over a [`Connection`], where storing at least -/// some data from the event is possible. -/// -/// This acts as a cache, to avoid making requests over the REST API through the -/// [`rest`] 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. -/// -/// # 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. -/// -/// This allows you to only need 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 `rest` 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 -/// [`Guild`]s. -/// -/// [`Connection`]: ../../client/struct.Connection.html -/// [`Context`]: ../../client/struct.Context.html -/// [`Context::get_channel`]: ../../client/struct.Context.html#method.get_channel -/// [`Guild`]: ../../model/struct.Guild.html -/// [`Role`]: ../../model/struct.Role.html -/// [`rest`]: ../../client/rest/index.html -#[derive(Clone, Debug)] -pub struct Cache { - /// A map of channels in [`Guild`]s that the current user has received data - /// for. - /// - /// When a [`Event::GuildDelete`] or [`Event::GuildUnavailable`] is - /// received and processed by the cache, the relevant channels are also - /// removed from this map. - /// - /// [`Event::GuildDelete`]: ../../model/event/struct.GuildDeleteEvent.html - /// [`Event::GuildUnavailable`]: ../../model/event/struct.GuildUnavailableEvent.html - /// [`Guild`]: ../../model/struct.Guild.html - pub channels: HashMap<ChannelId, Arc<RwLock<GuildChannel>>>, - /// A map of the groups that the current user is in. - /// - /// 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, Arc<RwLock<Group>>>, - /// 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, Arc<RwLock<Guild>>>, - /// A map of notes that a user has made for individual users. - /// - /// An empty note is equivalent to having no note, and creating an empty - /// note is equivalent to deleting a note. - /// - /// 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, Arc<RwLock<PrivateChannel>>>, - /// The total number of shards being used by the bot. - pub shard_count: u64, - /// 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: HashSet<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, - /// A map of users that the current user sees. - /// - /// Users are added to - and updated from - this map via the following - /// received events: - /// - /// - [`ChannelRecipientAdd`][`ChannelRecipientAddEvent`] - /// - [`GuildMemberAdd`][`GuildMemberAddEvent`] - /// - [`GuildMemberRemove`][`GuildMemberRemoveEvent`] - /// - [`GuildMembersChunk`][`GuildMembersChunkEvent`] - /// - [`GuildSync`][`GuildSyncEvent`] - /// - [`PresenceUpdate`][`PresenceUpdateEvent`] - /// - [`Ready`][`ReadyEvent`] - /// - /// Note, however, that users are _not_ removed from the map on removal - /// events such as [`GuildMemberRemove`][`GuildMemberRemoveEvent`], as other - /// structs such as members or recipients may still exist. - /// - /// [`ChannelRecipientAddEvent`]: ../../model/event/struct.ChannelRecipientAddEvent.html - /// [`GuildMemberAddEvent`]: ../../model/event/struct.GuildMemberAddEvent.html - /// [`GuildMemberRemoveEvent`]: ../../model/event/struct.GuildMemberRemoveEvent.html - /// [`GuildMemberUpdateEvent`]: ../../model/event/struct.GuildMemberUpdateEvent.html - /// [`GuildMembersChunkEvent`]: ../../model/event/struct.GuildMembersChunkEvent.html - /// [`GuildSyncEvent`]: ../../model/event/struct.GuildSyncEvent.html - /// [`PresenceUpdateEvent`]: ../../model/event/struct.PresenceUpdateEvent.html - /// [`Ready`]: ../../model/event/struct.ReadyEvent.html - pub users: HashMap<UserId, Arc<RwLock<User>>>, -} - -impl Cache { - /// Fetches 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::chunk_guilds`], and can be - /// used to determine how many members have not yet been downloaded. - /// - /// [`Member`]: ../../model/struct.Member.html - /// [`User`]: ../../model/struct.User.html - pub fn unknown_members(&self) -> u64 { - let mut total = 0; - - for guild in self.guilds.values() { - let guild = guild.read().unwrap(); - - let members = guild.members.len() as u64; - - if guild.member_count > members { - total += guild.member_count - members; - } - } - - total - } - - /// Fetches 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() - .cloned() - .chain(self.private_channels.keys().cloned()) - .collect() - } - - /// Fetches 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() - .map(|g| g.read().unwrap().id) - .chain(self.unavailable_guilds.iter().cloned()) - .collect() - } - - /// Retrieves a [`Channel`] from the cache based on the given Id. - /// - /// This will search the [`channels`] map, the [`private_channels`] map, and - /// then the map of [`groups`] to find the channel. - /// - /// If you know what type of channel you're looking for, you should instead - /// manually retrieve from one of the respective maps or methods: - /// - /// - [`GuildChannel`]: [`get_guild_channel`] or [`channels`] - /// - [`PrivateChannel`]: [`get_private_channel`] or [`private_channels`] - /// - [`Group`]: [`get_group`] or [`groups`] - /// - /// [`Channel`]: ../../model/enum.Channel.html - /// [`Group`]: ../../model/struct.Group.html - /// [`Guild`]: ../../model/struct.Guild.html - /// [`channels`]: #structfield.channels - /// [`get_group`]: #method.get_group - /// [`get_guild_channel`]: #method.get_guild_channel - /// [`get_private_channel`]: #method.get_private_channel - /// [`groups`]: #structfield.groups - /// [`private_channels`]: #structfield.private_channels - pub fn channel<C: Into<ChannelId>>(&self, id: C) -> Option<Channel> { - let id = id.into(); - - if let Some(channel) = self.channels.get(&id) { - return Some(Channel::Guild(channel.clone())); - } - - if let Some(private_channel) = self.private_channels.get(&id) { - return Some(Channel::Private(private_channel.clone())); - } - - if let Some(group) = self.groups.get(&id) { - return Some(Channel::Group(group.clone())); - } - - None - } - - /// Retrieves a guild from the cache based on the given Id. - /// - /// The only advantage of this method is that you can pass in anything that - /// is indirectly a [`GuildId`]. - /// - /// [`GuildId`]: ../../model/struct.GuildId.html - #[inline] - pub fn guild<G: Into<GuildId>>(&self, id: G) -> Option<Arc<RwLock<Guild>>> { - self.guilds.get(&id.into()).cloned() - } - - /// Retrieves a reference to a [`Guild`]'s channel. Unlike [`get_channel`], - /// this will only search guilds for the given channel. - /// - /// The only advantage of this method is that you can pass in anything that - /// is indirectly a [`ChannelId`]. - /// - /// # Examples - /// - /// Getting a guild's channel via the Id of the message received through a - /// [`Client::on_message`] event dispatch: - /// - /// ```rust,ignore - /// use serenity::client::CACHE; - /// - /// let cache = CACHE.read().unwrap(); - /// - /// let channel = match cache.get_guild_channel(message.channel_id) { - /// Some(channel) => channel, - /// None => { - /// if let Err(why) = message.channel_id.say("Could not find guild's channel data") { - /// println!("Error sending message: {:?}", why); - /// } - /// - /// return; - /// }, - /// }; - /// ``` - /// - /// [`ChannelId`]: ../../model/struct.ChannelId.html - /// [`Client::on_message`]: ../../client/struct.Client.html#method.on_message - /// [`Guild`]: ../../model/struct.Guild.html - /// [`get_channel`]: #method.get_channel - #[inline] - pub fn guild_channel<C: Into<ChannelId>>(&self, id: C) -> Option<Arc<RwLock<GuildChannel>>> { - self.channels.get(&id.into()).cloned() - } - - /// Retrieves a reference to a [`Group`] from the cache based on the given - /// associated channel Id. - /// - /// The only advantage of this method is that you can pass in anything that - /// is indirectly a [`ChannelId`]. - /// - /// [`ChannelId`]: ../../model/struct.ChannelId.html - /// [`Group`]: ../../model/struct.Group.html - #[inline] - pub fn group<C: Into<ChannelId>>(&self, id: C) -> Option<Arc<RwLock<Group>>> { - self.groups.get(&id.into()).cloned() - } - - /// Retrieves a [`Guild`]'s member from the cache based on the guild's and - /// user's given Ids. - /// - /// **Note**: This will clone the entire member. Instead, retrieve the guild - /// and retrieve from the guild's [`members`] map to avoid this. - /// - /// # Examples - /// - /// Retrieving the member object of the user that posted a message, in a - /// [`Client::on_message`] context: - /// - /// ```rust,ignore - /// use serenity::client::CACHE; - /// - /// 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) = message.channel_id.say("Error finding channel data") { - /// println!("Error sending message: {:?}", why); - /// } - /// }, - /// }; - /// - /// match cache.get_member(channel.guild_id, message.author.id) { - /// Some(member) => member, - /// None => { - /// if let Err(why) = message.channel_id.say("Error finding member data") { - /// println!("Error sending message: {:?}", why); - /// } - /// }, - /// } - /// }; - /// - /// let msg = format!("You have {} roles", member.roles.len()); - /// - /// if let Err(why) = message.channel_id.say(&msg) { - /// println!("Error sending message: {:?}", why); - /// } - /// ``` - /// - /// [`Client::on_message`]: ../../client/struct.Client.html#method.on_message - /// [`Guild`]: ../../model/struct.Guild.html - /// [`members`]: ../../model/struct.Guild.html#structfield.members - pub fn member<G, U>(&self, guild_id: G, user_id: U) -> Option<Member> - where G: Into<GuildId>, U: Into<UserId> { - self.guilds - .get(&guild_id.into()) - .and_then(|guild| guild.write().unwrap().members.get(&user_id.into()).cloned()) - } - - /// Retrieves a [`PrivateChannel`] from the cache's [`private_channels`] - /// map, if it exists. - /// - /// The only advantage of this method is that you can pass in anything that - /// is indirectly a [`ChannelId`]. - #[inline] - pub fn private_channel<C: Into<ChannelId>>(&self, channel_id: C) - -> Option<Arc<RwLock<PrivateChannel>>> { - self.private_channels.get(&channel_id.into()).cloned() - } - - /// Retrieves a [`Guild`]'s role by their Ids. - /// - /// **Note**: This will clone the entire role. Instead, retrieve the guild - /// and retrieve from the guild's [`roles`] map to avoid this. - /// - /// [`Guild`]: ../../model/struct.Guild.html - /// [`roles`]: ../../model/struct.Guild.html#structfield.roles - pub fn role<G, R>(&self, guild_id: G, role_id: R) -> Option<Role> - where G: Into<GuildId>, R: Into<RoleId> { - self.guilds - .get(&guild_id.into()) - .and_then(|g| g.read().unwrap().roles.get(&role_id.into()).cloned()) - } - - /// Retrieves a `User` from the cache's [`users`] map, if it exists. - /// - /// The only advantage of this method is that you can pass in anything that - /// is indirectly a [`UserId`]. - /// - /// [`UserId`]: ../../model/struct.UserId.html - /// [`users`]: #structfield.users - #[inline] - pub fn user<U: Into<UserId>>(&self, user_id: U) -> Option<Arc<RwLock<User>>> { - self.users.get(&user_id.into()).cloned() - } - - /// Alias of [`channel`]. - /// - /// [`channel`]: #method.channel - #[deprecated(since="0.1.5", note="Use `channel` instead.")] - #[inline] - pub fn get_channel<C: Into<ChannelId>>(&self, id: C) -> Option<Channel> { - self.channel(id) - } - - /// Alias of [`guild`]. - /// - /// [`guild`]: #method.guild - #[deprecated(since="0.1.5", note="Use `guild` instead.")] - #[inline] - pub fn get_guild<G: Into<GuildId>>(&self, id: G) -> Option<Arc<RwLock<Guild>>> { - self.guild(id) - } - - /// Alias of [`guild_channel`]. - /// - /// [`guild_channel`]: #method.guild_channel - #[deprecated(since="0.1.5", note="Use `guild_channel` instead.")] - #[inline] - pub fn get_guild_channel<C: Into<ChannelId>>(&self, id: C) - -> Option<Arc<RwLock<GuildChannel>>> { - self.guild_channel(id) - } - - /// Alias of [`member`]. - /// - /// [`member`]: #method.member - #[deprecated(since="0.1.5", note="Use `member` instead.")] - #[inline] - pub fn get_member<G, U>(&self, guild_id: G, user_id: U) -> Option<Member> - where G: Into<GuildId>, U: Into<UserId> { - self.member(guild_id, user_id) - } - - /// Alias of [`private_channel`]. - /// - /// [`private_channel`]: #method.private_channel - #[deprecated(since="0.1.5", note="Use `private_channel` instead.")] - #[inline] - pub fn get_private_channel<C: Into<ChannelId>>(&self, id: C) - -> Option<Arc<RwLock<PrivateChannel>>> { - self.private_channel(id) - } - - /// Alias of [`role`]. - /// - /// [`role`]: #method.role - #[deprecated(since="0.1.5", note="Use `role` instead.")] - #[inline] - pub fn get_role<G, R>(&self, guild_id: G, role_id: R) -> Option<Role> - where G: Into<GuildId>, R: Into<RoleId> { - self.role(guild_id, role_id) - } - - /// Alias of [`user`]. - /// - /// [`user`]: #method.user - #[deprecated(since="0.1.5", note="Use `user` instead.")] - #[inline] - pub fn get_user<U: Into<UserId>>(&self, id: U) -> Option<Arc<RwLock<User>>> { - self.user(id) - } - - #[doc(hidden)] - pub fn update_with_channel_create(&mut self, event: &ChannelCreateEvent) -> Option<Channel> { - match event.channel { - Channel::Group(ref group) => { - let group = group.clone(); - - let channel_id = { - let writer = group.write().unwrap(); - - for (recipient_id, recipient) in &mut group.write().unwrap().recipients { - self.update_user_entry(&recipient.read().unwrap()); - - *recipient = self.users[recipient_id].clone(); - } - - writer.channel_id - }; - - let ch = self.groups.insert(channel_id, group); - - ch.map(Channel::Group) - }, - Channel::Guild(ref channel) => { - let (guild_id, channel_id) = { - let channel = channel.read().unwrap(); - - (channel.guild_id, channel.id) - }; - - self.channels.insert(channel_id, channel.clone()); - - self.guilds - .get_mut(&guild_id) - .and_then(|guild| { - guild.write().unwrap().channels.insert(channel_id, channel.clone()) - }).map(Channel::Guild) - }, - Channel::Private(ref channel) => { - let channel = channel.clone(); - - let mut channel_writer = channel.write().unwrap(); - - let user_id = { - let user_reader = channel_writer.recipient.read().unwrap(); - - self.update_user_entry(&user_reader); - - user_reader.id - }; - - channel_writer.recipient = self.users[&user_id].clone(); - - let ch = self.private_channels.insert(channel_writer.id, channel.clone()); - ch.map(Channel::Private) - }, - } - } - - #[doc(hidden)] - pub fn update_with_channel_delete(&mut self, event: &ChannelDeleteEvent) -> Option<Channel> { - match event.channel { - Channel::Group(ref group) => { - self.groups.remove(&group.read().unwrap().channel_id).map(Channel::Group) - }, - Channel::Private(ref channel) => { - self.private_channels.remove(&channel.read().unwrap().id).map(Channel::Private) - }, - Channel::Guild(ref channel) => { - let (channel_id, guild_id) = { - let channel = channel.read().unwrap(); - - (channel.id, channel.guild_id) - }; - - self.channels.remove(&channel_id); - - self.guilds - .get_mut(&guild_id) - .and_then(|guild| guild.write().unwrap().channels.remove(&channel_id)) - .map(Channel::Guild) - }, - } - } - - #[doc(hidden)] - pub fn update_with_channel_pins_update(&mut self, event: &ChannelPinsUpdateEvent) { - if let Some(channel) = self.channels.get(&event.channel_id) { - channel.write().unwrap().last_pin_timestamp = event.last_pin_timestamp.clone(); - - return; - } - - if let Some(channel) = self.private_channels.get_mut(&event.channel_id) { - channel.write().unwrap().last_pin_timestamp = event.last_pin_timestamp.clone(); - - return; - } - - if let Some(group) = self.groups.get_mut(&event.channel_id) { - group.write().unwrap().last_pin_timestamp = event.last_pin_timestamp.clone(); - - return; - } - } - - #[doc(hidden)] - pub fn update_with_channel_recipient_add(&mut self, event: &mut ChannelRecipientAddEvent) { - self.update_user_entry(&event.user); - let user = self.users[&event.user.id].clone(); - - self.groups - .get_mut(&event.channel_id) - .map(|group| { - group.write() - .unwrap() - .recipients - .insert(event.user.id, user); - }); - } - - #[doc(hidden)] - pub fn update_with_channel_recipient_remove(&mut self, event: &ChannelRecipientRemoveEvent) { - self.groups - .get_mut(&event.channel_id) - .map(|group| group.write().unwrap().recipients.remove(&event.user.id)); - } - - #[doc(hidden)] - pub fn update_with_channel_update(&mut self, event: &ChannelUpdateEvent) { - match event.channel { - Channel::Group(ref group) => { - let (ch_id, no_recipients) = { - let group = group.read().unwrap(); - - (group.channel_id, group.recipients.is_empty()) - }; - - match self.groups.entry(ch_id) { - Entry::Vacant(e) => { - e.insert(group.clone()); - }, - Entry::Occupied(mut e) => { - let mut dest = e.get_mut().write().unwrap(); - - if no_recipients { - let recipients = mem::replace(&mut dest.recipients, HashMap::new()); - - dest.clone_from(&group.read().unwrap()); - - dest.recipients = recipients; - } else { - dest.clone_from(&group.read().unwrap()); - } - }, - } - }, - Channel::Guild(ref channel) => { - let (channel_id, guild_id) = { - let channel = channel.read().unwrap(); - - (channel.id, channel.guild_id) - }; - - self.channels.insert(channel_id, channel.clone()); - self.guilds - .get_mut(&guild_id) - .map(|guild| { - guild.write() - .unwrap() - .channels - .insert(channel_id, channel.clone()) - }); - }, - Channel::Private(ref channel) => { - self.private_channels - .get_mut(&channel.read().unwrap().id) - .map(|private| private.clone_from(channel)); - }, - } - } - - #[doc(hidden)] - pub fn update_with_guild_create(&mut self, event: &GuildCreateEvent) { - self.unavailable_guilds.remove(&event.guild.id); - - let mut guild = event.guild.clone(); - - for (user_id, member) in &mut guild.members { - self.update_user_entry(&member.user.read().unwrap()); - let user = self.users[user_id].clone(); - - member.user = user.clone(); - } - - self.channels.extend(guild.channels.clone()); - self.guilds.insert(event.guild.id, Arc::new(RwLock::new(guild))); - } - - #[doc(hidden)] - pub fn update_with_guild_delete(&mut self, event: &GuildDeleteEvent) - -> Option<Arc<RwLock<Guild>>> { - // Remove channel entries for the guild if the guild is found. - self.guilds.remove(&event.guild.id).map(|guild| { - for channel_id in guild.read().unwrap().channels.keys() { - self.channels.remove(channel_id); - } - - guild - }) - } - - #[doc(hidden)] - pub fn update_with_guild_emojis_update(&mut self, event: &GuildEmojisUpdateEvent) { - self.guilds - .get_mut(&event.guild_id) - .map(|guild| guild.write().unwrap().emojis.extend(event.emojis.clone())); - } - - #[doc(hidden)] - pub fn update_with_guild_member_add(&mut self, event: &mut GuildMemberAddEvent) { - let user_id = event.member.user.read().unwrap().id; - self.update_user_entry(&event.member.user.read().unwrap()); - - // Always safe due to being inserted above. - event.member.user = self.users[&user_id].clone(); - - self.guilds - .get_mut(&event.guild_id) - .map(|guild| { - let mut guild = guild.write().unwrap(); - - guild.member_count += 1; - guild.members.insert(user_id, event.member.clone()); - }); - } - - #[doc(hidden)] - pub fn update_with_guild_member_remove(&mut self, event: &GuildMemberRemoveEvent) - -> Option<Member> { - self.guilds - .get_mut(&event.guild_id) - .and_then(|guild| { - let mut guild = guild.write().unwrap(); - - guild.member_count -= 1; - guild.members.remove(&event.user.id) - }) - } - - #[doc(hidden)] - pub fn update_with_guild_member_update(&mut self, event: &GuildMemberUpdateEvent) - -> Option<Member> { - self.update_user_entry(&event.user); - - if let Some(guild) = self.guilds.get_mut(&event.guild_id) { - let mut guild = guild.write().unwrap(); - - let mut found = false; - - let item = if let Some(member) = guild.members.get_mut(&event.user.id) { - let item = Some(member.clone()); - - member.nick.clone_from(&event.nick); - member.roles.clone_from(&event.roles); - member.user.write().unwrap().clone_from(&event.user); - - found = true; - - item - } else { - None - }; - - if !found { - guild.members.insert(event.user.id, Member { - deaf: false, - guild_id: Some(event.guild_id), - joined_at: String::default(), - mute: false, - nick: event.nick.clone(), - roles: event.roles.clone(), - user: Arc::new(RwLock::new(event.user.clone())), - }); - } - - item - } else { - None - } - } - - #[doc(hidden)] - pub fn update_with_guild_members_chunk(&mut self, event: &GuildMembersChunkEvent) { - for member in event.members.values() { - self.update_user_entry(&member.user.read().unwrap()); - } - - self.guilds - .get_mut(&event.guild_id) - .map(|guild| guild.write().unwrap().members.extend(event.members.clone())); - } - - #[doc(hidden)] - pub fn update_with_guild_role_create(&mut self, event: &GuildRoleCreateEvent) { - self.guilds - .get_mut(&event.guild_id) - .map(|guild| guild.write().unwrap().roles.insert(event.role.id, event.role.clone())); - } - - #[doc(hidden)] - pub fn update_with_guild_role_delete(&mut self, event: &GuildRoleDeleteEvent) -> Option<Role> { - self.guilds - .get_mut(&event.guild_id) - .and_then(|guild| guild.write().unwrap().roles.remove(&event.role_id)) - } - - #[doc(hidden)] - pub fn update_with_guild_role_update(&mut self, event: &GuildRoleUpdateEvent) -> Option<Role> { - self.guilds - .get_mut(&event.guild_id) - .and_then(|guild| { - guild.write() - .unwrap() - .roles - .get_mut(&event.role.id) - .map(|role| mem::replace(role, event.role.clone())) - }) - } - - #[doc(hidden)] - pub fn update_with_guild_unavailable(&mut self, event: &GuildUnavailableEvent) { - self.unavailable_guilds.insert(event.guild_id); - self.guilds.remove(&event.guild_id); - } - - #[doc(hidden)] - pub fn update_with_guild_update(&mut self, event: &GuildUpdateEvent) { - self.guilds - .get_mut(&event.guild.id) - .map(|guild| { - let mut guild = guild.write().unwrap(); - - guild.afk_timeout = event.guild.afk_timeout; - guild.afk_channel_id.clone_from(&event.guild.afk_channel_id); - guild.icon.clone_from(&event.guild.icon); - guild.name.clone_from(&event.guild.name); - guild.owner_id.clone_from(&event.guild.owner_id); - guild.region.clone_from(&event.guild.region); - guild.roles.clone_from(&event.guild.roles); - guild.verification_level = event.guild.verification_level; - }); - } - - #[doc(hidden)] - pub fn update_with_presences_replace(&mut self, event: &PresencesReplaceEvent) { - self.presences.extend({ - let mut p: HashMap<UserId, Presence> = HashMap::default(); - - for presence in &event.presences { - p.insert(presence.user_id, presence.clone()); - } - - p - }); - } - - #[doc(hidden)] - pub fn update_with_presence_update(&mut self, event: &mut PresenceUpdateEvent) { - let user_id = event.presence.user_id; - - if let Some(user) = event.presence.user.as_mut() { - self.update_user_entry(&user.read().unwrap()); - *user = self.users[&user_id].clone(); - } - - if let Some(guild_id) = event.guild_id { - if let Some(guild) = self.guilds.get_mut(&guild_id) { - let mut guild = guild.write().unwrap(); - - // If the member went offline, remove them from the presence list. - if event.presence.status == OnlineStatus::Offline { - guild.presences.remove(&event.presence.user_id); - } else { - guild.presences.insert(event.presence.user_id, event.presence.clone()); - } - } - } else if event.presence.status == OnlineStatus::Offline { - self.presences.remove(&event.presence.user_id); - } else { - self.presences.insert(event.presence.user_id, event.presence.clone()); - } - } - - #[doc(hidden)] - pub fn update_with_ready(&mut self, event: &ReadyEvent) { - let mut ready = event.ready.clone(); - - for guild in ready.guilds { - match guild { - GuildStatus::Offline(unavailable) => { - self.guilds.remove(&unavailable.id); - self.unavailable_guilds.insert(unavailable.id); - }, - GuildStatus::OnlineGuild(guild) => { - self.unavailable_guilds.remove(&guild.id); - self.guilds.insert(guild.id, Arc::new(RwLock::new(guild))); - }, - GuildStatus::OnlinePartialGuild(_) => {}, - } - } - - // The private channels sent in the READY contains both the actual - // private channels and the groups. - for (channel_id, channel) in ready.private_channels { - match channel { - Channel::Group(group) => { - self.groups.insert(channel_id, group); - }, - Channel::Private(channel) => { - self.private_channels.insert(channel_id, channel); - }, - Channel::Guild(guild) => warn!("Got a guild in DMs: {:?}", guild), - } - } - - for (user_id, presence) in &mut ready.presences { - if let Some(ref user) = presence.user { - self.update_user_entry(&user.read().unwrap()); - } - - presence.user = self.users.get(user_id).cloned(); - } - - self.presences.extend(ready.presences); - self.shard_count = ready.shard.map_or(1, |s| s[1]); - self.user = ready.user; - } - - #[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 { - if let Some(guild) = self.guilds.get_mut(&guild_id) { - let mut guild = guild.write().unwrap(); - - if event.voice_state.channel_id.is_some() { - // Update or add to the voice state list - { - let finding = guild.voice_states.get_mut(&event.voice_state.user_id); - - if let Some(srv_state) = finding { - srv_state.clone_from(&event.voice_state); - - return; - } - } - - guild.voice_states.insert(event.voice_state.user_id, event.voice_state.clone()); - } else { - // Remove the user from the voice state list - guild.voice_states.remove(&event.voice_state.user_id); - } - } - - return; - } - } - - // Adds or updates a user entry in the [`users`] map with a received user. - // - // [`users`]: #structfield.users - fn update_user_entry(&mut self, user: &User) { - match self.users.entry(user.id) { - Entry::Vacant(e) => { - e.insert(Arc::new(RwLock::new(user.clone()))); - }, - Entry::Occupied(mut e) => { - e.get_mut().write().unwrap().clone_from(user); - } - } - } -} - -impl Default for Cache { - fn default() -> Cache { - Cache { - channels: HashMap::default(), - groups: HashMap::default(), - guilds: HashMap::default(), - notes: HashMap::default(), - presences: HashMap::default(), - private_channels: HashMap::default(), - shard_count: 1, - unavailable_guilds: HashSet::default(), - user: CurrentUser { - avatar: None, - bot: false, - discriminator: 0, - email: None, - id: UserId(0), - mfa_enabled: false, - name: String::default(), - verified: false, - }, - users: HashMap::default(), - } - } -} |