From d4726f899cf6b86d805a8a2a77ec57782ec3bdd6 Mon Sep 17 00:00:00 2001 From: Austin Hellyer Date: Tue, 22 Nov 2016 07:57:51 -0800 Subject: Rename the State to Cache --- src/client/context.rs | 56 +-- src/client/dispatch.rs | 66 ++-- src/client/error.rs | 10 +- src/client/event_store.rs | 52 +-- src/client/mod.rs | 60 ++-- src/ext/cache/mod.rs | 853 ++++++++++++++++++++++++++++++++++++++++++++ src/ext/mod.rs | 6 +- src/ext/state/mod.rs | 853 -------------------------------------------- src/ext/voice/connection.rs | 4 +- src/lib.rs | 10 +- src/model/channel.rs | 16 +- src/model/gateway.rs | 4 +- src/model/guild.rs | 30 +- src/model/id.rs | 18 +- src/model/invite.rs | 16 +- src/model/mod.rs | 4 +- src/model/user.rs | 22 +- src/model/utils.rs | 14 +- src/utils/macros.rs | 94 ++--- 19 files changed, 1094 insertions(+), 1094 deletions(-) create mode 100644 src/ext/cache/mod.rs delete mode 100644 src/ext/state/mod.rs (limited to 'src') diff --git a/src/client/context.rs b/src/client/context.rs index 04f08e0..67c0146 100644 --- a/src/client/context.rs +++ b/src/client/context.rs @@ -20,8 +20,8 @@ use ::internal::prelude::*; use ::model::*; use ::utils; -#[cfg(feature = "state")] -use super::STATE; +#[cfg(feature = "cache")] +use super::CACHE; /// The context is a general utility struct provided on event dispatches, which /// helps with dealing with the current "context" of the event dispatch, @@ -41,12 +41,12 @@ use super::STATE; /// A context will only live for the event it was dispatched for. After the /// event handler finished, it is destroyed and will not be re-used. /// -/// # Automatically using the State +/// # Automatically using the Cache /// -/// The context makes use of the [`State`] being global, and will first check -/// the state for associated data before hitting the REST API. This is to save +/// The context makes use of the [`Cache`] being global, and will first check +/// the cache for associated data before hitting the REST API. This is to save /// Discord requests, and ultimately save your bot bandwidth and time. This also -/// acts as a clean interface for retrieving from the state without needing to +/// acts as a clean interface for retrieving from the cache without needing to /// check it yourself first, and then performing a request if it does not exist. /// The context ultimately acts as a means to simplify these two operations into /// one. @@ -54,13 +54,13 @@ use super::STATE; /// For example, if you are needing information about a /// [channel][`PublicChannel`] within a [guild][`LiveGuild`], then you can /// use [`get_channel`] to retrieve it. Under most circumstances, the guild and -/// its channels will be cached within the state, and `get_channel` will just -/// pull from the state. If it does not exist, it will make a request to the -/// REST API, and then insert a clone of the channel into the state, returning +/// its channels will be cached within the cache, and `get_channel` will just +/// pull from the cache. If it does not exist, it will make a request to the +/// REST API, and then insert a clone of the channel into the cache, returning /// you the channel. /// -/// In this scenario, now that the state has the channel, performing the same -/// request to `get_channel` will instead pull from the state, as it is now +/// In this scenario, now that the cache has the channel, performing the same +/// request to `get_channel` will instead pull from the cache, as it is now /// cached. /// /// [`Channel`]: ../model/enum.Channel.html @@ -69,7 +69,7 @@ use super::STATE; /// [`Message`]: ../model/struct.Message.html /// [`PublicChannel`]: ../model/struct.PublicChannel.html /// [`Shard`]: gateway/struct.Shard.html -/// [`State`]: ../ext/state/struct.State.html +/// [`Cache`]: ../ext/cache/struct.Cache.html /// [`get_channel`]: #method.get_channel /// [`http`]: http/index.html /// [`say`]: #method.say @@ -761,11 +761,11 @@ impl Context { let guild_id = guild_id.into(); let role_id = role_id.into(); - feature_state! {{ - let state = STATE.lock().unwrap(); + feature_cache! {{ + let cache = CACHE.lock().unwrap(); let role = if let Some(role) = { - state.get_role(guild_id.0, role_id.0) + cache.get_role(guild_id.0, role_id.0) } { role } else { @@ -824,8 +824,8 @@ impl Context { where C: Into { let channel_id = channel_id.into(); - feature_state_enabled! {{ - if let Some(channel) = STATE.lock().unwrap().get_channel(channel_id) { + feature_cache_enabled! {{ + if let Some(channel) = CACHE.lock().unwrap().get_channel(channel_id) { return Ok(channel.clone()); } }} @@ -837,10 +837,10 @@ impl Context { -> Result> where G: Into { let guild_id = guild_id.into(); - feature_state_enabled! {{ - let state = STATE.lock().unwrap(); + feature_cache_enabled! {{ + let cache = CACHE.lock().unwrap(); - if let Some(guild) = state.get_guild(guild_id) { + if let Some(guild) = cache.get_guild(guild_id) { return Ok(guild.channels.clone()); } }} @@ -902,10 +902,10 @@ impl Context { let guild_id = guild_id.into(); let user_id = user_id.into(); - feature_state_enabled! {{ - let state = STATE.lock().unwrap(); + feature_cache_enabled! {{ + let cache = CACHE.lock().unwrap(); - if let Some(member) = state.get_member(guild_id, user_id) { + if let Some(member) = cache.get_member(guild_id, user_id) { return Ok(member.clone()); } }} @@ -1132,7 +1132,7 @@ impl Context { /// information was generated by them. /// /// ```rust,no_run - /// use serenity::client::{STATE, Client, Context}; + /// use serenity::client::{CACHE, Client, Context}; /// use serenity::model::{Channel, Message}; /// use serenity::utils::Colour; /// use std::env; @@ -1149,8 +1149,8 @@ impl Context { /// let _ = client.start(); /// /// fn ping(context: Context, message: Message, _arguments: Vec) { - /// let state = STATE.lock().unwrap(); - /// let ch = state.get_channel(message.channel_id); + /// let cache = CACHE.lock().unwrap(); + /// let ch = cache.get_channel(message.channel_id); /// let name = match ch { /// Some(Channel::Public(ch)) => ch.name.clone(), /// _ => "Unknown".to_owned(), @@ -1181,9 +1181,9 @@ impl Context { /// .name("Channel name:") /// .value(&name)) /// .footer(|mut f| { - /// f = f.text(&format!("Generated by {}", state.user.name)); + /// f = f.text(&format!("Generated by {}", cache.user.name)); /// - /// if let Some(avatar) = state.user.avatar_url() { + /// if let Some(avatar) = cache.user.avatar_url() { /// f = f.icon_url(&avatar); /// } /// diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs index 96c8e69..b1e593e 100644 --- a/src/client/dispatch.rs +++ b/src/client/dispatch.rs @@ -9,8 +9,8 @@ use ::model::{ChannelId, Event, Message}; #[cfg(feature="framework")] use ::ext::framework::Framework; -#[cfg(feature = "state")] -use super::STATE; +#[cfg(feature = "cache")] +use super::CACHE; macro_rules! handler { ($field:ident, $event_store:ident) => { @@ -24,13 +24,13 @@ macro_rules! handler { macro_rules! update { ($method:ident, $event:expr) => { - feature_state_enabled! {{ - STATE.lock().unwrap().$method(&$event) + feature_cache_enabled! {{ + CACHE.lock().unwrap().$method(&$event) }} }; ($method:ident, $event:expr, $old:expr) => { - feature_state_enabled! {{ - STATE.lock().unwrap().$method(&$event, $old) + feature_cache_enabled! {{ + CACHE.lock().unwrap().$method(&$event, $old) }} }; } @@ -123,7 +123,7 @@ fn handle_event(event: Event, let context = context(None, conn, login_type); let handler = handler.clone(); - feature_state! {{ + feature_cache! {{ let call = update!(update_with_call_delete, event); thread::spawn(move || { @@ -143,9 +143,9 @@ fn handle_event(event: Event, let context = context(None, conn, login_type); let handler = handler.clone(); - feature_state! {{ + feature_cache! {{ let before = update!(update_with_call_update, event, true); - let after = STATE + let after = CACHE .lock() .unwrap() .calls @@ -161,7 +161,7 @@ fn handle_event(event: Event, }); }} } else { - feature_state_enabled! {{ + feature_cache_enabled! {{ update!(update_with_call_update, event, false); }} } @@ -253,8 +253,8 @@ fn handle_event(event: Event, login_type); let handler = handler.clone(); - feature_state! {{ - let before = STATE.lock() + feature_cache! {{ + let before = CACHE.lock() .unwrap() .get_channel(event.channel.id()); update!(update_with_channel_update, event); @@ -328,7 +328,7 @@ fn handle_event(event: Event, let context = context(None, conn, login_type); let handler = handler.clone(); - feature_state! {{ + feature_cache! {{ let full = update!(update_with_guild_delete, event); thread::spawn(move || { @@ -340,7 +340,7 @@ fn handle_event(event: Event, }); }} } else { - feature_state_enabled! {{ + feature_cache_enabled! {{ let _full = update!(update_with_guild_delete, event); }} } @@ -384,7 +384,7 @@ fn handle_event(event: Event, let context = context(None, conn, login_type); let handler = handler.clone(); - feature_state! {{ + feature_cache! {{ let member = update!(update_with_guild_member_remove, event); thread::spawn(move || { @@ -396,7 +396,7 @@ fn handle_event(event: Event, }); }} } else { - feature_state_enabled! {{ + feature_cache_enabled! {{ let _member = update!(update_with_guild_member_remove, event); }} } @@ -406,13 +406,13 @@ fn handle_event(event: Event, let context = context(None, conn, login_type); let handler = handler.clone(); - feature_state! {{ + feature_cache! {{ let before = update!(update_with_guild_member_update, event); // This is safe, as the update would have created the member // if it did not exist. Thus, there _should_ be no way that this // could fail under any circumstance. - let after = STATE.lock() + let after = CACHE.lock() .unwrap() .get_member(event.guild_id, event.user.id) .unwrap() @@ -459,7 +459,7 @@ fn handle_event(event: Event, let context = context(None, conn, login_type); let handler = handler.clone(); - feature_state! {{ + feature_cache! {{ let role = update!(update_with_guild_role_delete, event); thread::spawn(move || { @@ -471,7 +471,7 @@ fn handle_event(event: Event, }); }} } else { - feature_state_enabled! {{ + feature_cache_enabled! {{ let _role = update!(update_with_guild_role_delete, event); }} } @@ -481,7 +481,7 @@ fn handle_event(event: Event, let context = context(None, conn, login_type); let handler = handler.clone(); - feature_state! {{ + feature_cache! {{ let before = update!(update_with_guild_role_update, event); thread::spawn(move || { @@ -493,7 +493,7 @@ fn handle_event(event: Event, }); }} } else { - feature_state_enabled! {{ + feature_cache_enabled! {{ let _before = update!(update_with_guild_role_update, event); }} } @@ -525,8 +525,8 @@ fn handle_event(event: Event, let context = context(None, conn, login_type); let handler = handler.clone(); - feature_state! {{ - let before = STATE.lock() + feature_cache! {{ + let before = CACHE.lock() .unwrap() .guilds .get(&event.guild.id) @@ -730,7 +730,7 @@ fn handle_event(event: Event, let context = context(None, conn, login_type); let handler = handler.clone(); - feature_state! {{ + feature_cache! {{ let before = update!(update_with_user_guild_settings_update, event); thread::spawn(move || { @@ -742,7 +742,7 @@ fn handle_event(event: Event, }); }} } else { - feature_state_enabled! {{ + feature_cache_enabled! {{ let _before = update!(update_with_user_guild_settings_update, event); }} } @@ -752,7 +752,7 @@ fn handle_event(event: Event, let context = context(None, conn, login_type); let handler = handler.clone(); - feature_state! {{ + feature_cache! {{ let before = update!(update_with_user_note_update, event); thread::spawn(move || { @@ -764,7 +764,7 @@ fn handle_event(event: Event, }); }} } else { - feature_state_enabled! {{ + feature_cache_enabled! {{ let _before = update!(update_with_user_note_update, event); }} } @@ -774,9 +774,9 @@ fn handle_event(event: Event, let context = context(None, conn, login_type); let handler = handler.clone(); - feature_state! {{ + feature_cache! {{ let before = update!(update_with_user_settings_update, event, true); - let after = STATE.lock().unwrap().settings.clone(); + let after = CACHE.lock().unwrap().settings.clone(); thread::spawn(move || { (handler)(context, before.unwrap(), after.unwrap()); @@ -787,7 +787,7 @@ fn handle_event(event: Event, }); }} } else { - feature_state_enabled! {{ + feature_cache_enabled! {{ update!(update_with_user_settings_update, event, false); }} } @@ -797,7 +797,7 @@ fn handle_event(event: Event, let context = context(None, conn, login_type); let handler = handler.clone(); - feature_state! {{ + feature_cache! {{ let before = update!(update_with_user_update, event); thread::spawn(move || { @@ -809,7 +809,7 @@ fn handle_event(event: Event, }); }} } else { - feature_state_enabled! {{ + feature_cache_enabled! {{ let _before = update!(update_with_user_update, event); }} } diff --git a/src/client/error.rs b/src/client/error.rs index 9b542ec..a016e92 100644 --- a/src/client/error.rs +++ b/src/client/error.rs @@ -60,11 +60,11 @@ pub enum Error { /// When there was an error retrieving the gateway URI from the REST API. Gateway, /// An indication that a [guild][`LiveGuild`] could not be found by - /// [Id][`GuildId`] in the [`State`]. + /// [Id][`GuildId`] in the [`Cache`]. /// /// [`GuildId`]: ../model/struct.GuildId.html /// [`LiveGuild`]: ../model/struct.LiveGuild.html - /// [`State`]: ../ext/state/struct.State.html + /// [`Cache`]: ../ext/cache/struct.Cache.html GuildNotFound, InvalidOpCode, /// When attempting to perform an action which is only available to user @@ -92,10 +92,10 @@ pub enum Error { /// /// [current user]: ../model/struct.CurrentUser.html InvalidUser, - /// An indicator that an item is missing from the [`State`], and the action + /// An indicator that an item is missing from the [`Cache`], and the action /// can not be continued. /// - /// [`State`]: ../ext/state/struct.State.html + /// [`Cache`]: ../ext/cache/struct.Cache.html ItemMissing, /// Indicates that a [`Message`]s content was too long and will not /// successfully send, as the length is over 2000 codepoints, or 4000 bytes. @@ -117,7 +117,7 @@ pub enum Error { /// When the decoding of a ratelimit header could not be properly decoded /// from UTF-8. RateLimitUtf8, - /// When attempting to find a required record from the State could not be + /// When attempting to find a required record from the Cache could not be /// found. This is required in methods such as [`Context::edit_role`]. /// /// [`Context::edit_role`]: struct.Context.html#method.edit_role diff --git a/src/client/event_store.rs b/src/client/event_store.rs index 9558ba1..0deaa94 100644 --- a/src/client/event_store.rs +++ b/src/client/event_store.rs @@ -26,13 +26,13 @@ use ::model::*; #[derive(Default)] pub struct EventStore { pub on_call_create: Option>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_call_delete: Option) + Send + Sync + 'static>>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_call_delete: Option>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_call_update: Option, Option) + Send + Sync + 'static>>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_call_update: Option>, pub on_channel_create: Option>, pub on_channel_delete: Option>, @@ -40,45 +40,45 @@ pub struct EventStore { pub on_channel_pins_update: Option>, pub on_channel_recipient_addition: Option>, pub on_channel_recipient_removal: Option>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_channel_update: Option, Channel) + Send + Sync + 'static>>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_channel_update: Option>, pub on_friend_suggestion_create: Option) + Send + Sync + 'static>>, pub on_friend_suggestion_delete: Option>, pub on_guild_ban_addition: Option>, pub on_guild_ban_removal: Option>, pub on_guild_create: Option>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_guild_delete: Option) + Send + Sync + 'static>>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_guild_delete: Option>, pub on_guild_emojis_update: Option) + Send + Sync + 'static>>, pub on_guild_integrations_update: Option>, pub on_guild_member_addition: Option>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_guild_member_removal: Option) + Send + Sync + 'static>>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_guild_member_removal: Option>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_guild_member_update: Option, Member) + Send + Sync + 'static>>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_guild_member_update: Option>, pub on_guild_members_chunk: Option) + Send + Sync + 'static>>, pub on_guild_role_create: Option>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_guild_role_delete: Option) + Send + Sync + 'static>>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_guild_role_delete: Option>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_guild_role_update: Option, Role) + Send + Sync + 'static>>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_guild_role_update: Option>, pub on_guild_sync: Option>, pub on_guild_unavailable: Option>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_guild_update: Option, Guild) + Send + Sync + 'static>>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_guild_update: Option>, pub on_message: Option>, pub on_message_ack: Option) + Send + Sync + 'static>>, @@ -88,9 +88,9 @@ pub struct EventStore { pub on_reaction_remove: Option>, pub on_reaction_remove_all: Option>, pub on_message_update: Option>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_note_update: Option, String) + Send + Sync + 'static>>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_note_update: Option>, pub on_presence_replace: Option) + Send + Sync + 'static>>, pub on_presence_update: Option>, @@ -100,17 +100,17 @@ pub struct EventStore { pub on_resume: Option>, pub on_typing_start: Option>, pub on_unknown: Option) + Send + Sync + 'static>>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_user_guild_settings_update: Option, UserGuildSettings) + Send + Sync + 'static>>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_user_guild_settings_update: Option>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_user_update: Option>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_user_update: Option>, - #[cfg(feature = "state")] + #[cfg(feature = "cache")] pub on_user_settings_update: Option>, - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] pub on_user_settings_update: Option>, pub on_voice_server_update: Option>, pub on_voice_state_update: Option, VoiceState) + Send + Sync + 'static>>, diff --git a/src/client/mod.rs b/src/client/mod.rs index d9c3ce2..2a9793f 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,7 +1,7 @@ //! The Client contains information about a single bot or user's token, as well //! as event handlers. Dispatching events to configured handlers and starting //! the shards' connections are handled directly via the client. In addition, -//! the [`http`] module and [`State`] are also automatically handled by the +//! the [`http`] module and [`Cache`] are also automatically handled by the //! Client module for you. //! //! A [`Context`] is provided for every handler. The context is an ergonomic @@ -10,14 +10,14 @@ //! The `http` module is the lower-level method of interacting with the Discord //! REST API. Realistically, there should be little reason to use this yourself, //! as the Context will do this for you. A possible use case of using the `http` -//! module is if you do not have a State, for purposes such as low memory +//! module is if you do not have a Cache, for purposes such as low memory //! requirements. //! //! Click [here][Client examples] for an example on how to use a `Client`. //! //! [`Client`]: struct.Client.html#examples //! [`Context`]: struct.Context.html -//! [`State`]: ../ext/state/index.html +//! [`Cache`]: ../ext/cache/index.html //! [`http`]: http/index.html //! [Client examples]: struct.Client.html#examples @@ -51,33 +51,33 @@ use ::model::*; #[cfg(feature = "framework")] use ::ext::framework::Framework; -#[cfg(feature = "state")] -use ::ext::state::State; +#[cfg(feature = "cache")] +use ::ext::cache::Cache; -#[cfg(feature = "state")] +#[cfg(feature = "cache")] lazy_static! { - /// The STATE is a mutable lazily-initialized static binding. It can be + /// The CACHE is a mutable lazily-initialized static binding. It can be /// accessed across any function and in any context. /// - /// This [`State`] instance is updated for every event received, so you do - /// not need to maintain your own state. + /// This [`Cache`] instance is updated for every event received, so you do + /// not need to maintain your own cache. /// - /// See the [state module documentation] for more details. + /// See the [cache module documentation] for more details. /// /// # Examples /// /// Retrieve the [current user][`CurrentUser`]'s Id: /// /// ```rust,ignore - /// use serenity::client::STATE; + /// use serenity::client::CACHE; /// - /// println!("{}", STATE.lock().unwrap().user.id); + /// println!("{}", CACHE.lock().unwrap().user.id); /// ``` /// /// [`CurrentUser`]: ../model/struct.CurrentUser.html - /// [`State`]: ../ext/state/struct.State.html - /// [state module documentation]: ../ext/state/index.html - pub static ref STATE: Arc> = Arc::new(Mutex::new(State::default())); + /// [`Cache`]: ../ext/cache/struct.Cache.html + /// [cache module documentation]: ../ext/cache/index.html + pub static ref CACHE: Arc> = Arc::new(Mutex::new(Cache::default())); } /// The Client is the way to "login" and be able to start sending authenticated @@ -729,8 +729,8 @@ impl Client { Ok((shard, ready, receiver)) => { self.shards.push(Arc::new(Mutex::new(shard))); - feature_state_enabled! {{ - STATE.lock() + feature_cache_enabled! {{ + CACHE.lock() .unwrap() .update_with_ready(&ready); }} @@ -801,12 +801,12 @@ impl Client { } } -#[cfg(feature = "state")] +#[cfg(feature = "cache")] impl Client { /// Attaches a handler for when a [`CallDelete`] is received. /// /// The `ChannelId` is the Id of the channel hosting the call. Returns the - /// call from the state - optionally - if the call was in it. + /// call from the cache - optionally - if the call was in it. /// /// [`CallDelete`]: ../model/enum.Event.html#variant.CallDelete pub fn on_call_delete(&mut self, handler: F) @@ -842,15 +842,15 @@ impl Client { /// /// Returns a partial guild as well as - optionally - the full guild, with /// data like [`Role`]s. This can be `None` in the event that it was not in - /// the [`State`]. + /// the [`Cache`]. /// - /// **Note**: The relevant guild is _removed_ from the State when this event + /// **Note**: The relevant guild is _removed_ from the Cache when this event /// is received. If you need to keep it, you can either re-insert it - /// yourself back into the State or manage it in another way. + /// yourself back into the Cache or manage it in another way. /// /// [`GuildDelete`]: ../model/enum.Event.html#variant.GuildDelete /// [`Role`]: ../model/struct.Role.html - /// [`State`]: ../ext/state/struct.State.html + /// [`Cache`]: ../ext/cache/struct.Cache.html pub fn on_guild_delete(&mut self, handler: F) where F: Fn(Context, Guild, Option) + Send + Sync + 'static { self.event_store.lock() @@ -861,7 +861,7 @@ impl Client { /// Attaches a handler for when a [`GuildMemberRemove`] is received. /// /// Returns the user's associated `Member` object, _if_ it existed in the - /// state. + /// cache. /// /// [`GuildMemberRemove`]: ../model/enum.Event.html#variant.GuildMemberRemove pub fn on_guild_member_remove(&mut self, handler: F) @@ -894,10 +894,10 @@ impl Client { /// Attaches a handler for when a [`GuildRoleUpdate`] is received. /// /// The optional `Role` is the role prior to updating. This can be `None` if - /// it did not exist in the [`State`] before the update. + /// it did not exist in the [`Cache`] before the update. /// /// [`GuildRoleUpdate`]: ../model/enum.Event.html#variant.GuildRoleUpdate - /// [`State`]: ../ext/state/struct.State.html + /// [`Cache`]: ../ext/cache/struct.Cache.html pub fn on_guild_role_update(&mut self, handler: F) where F: Fn(Context, GuildId, Option, Role) + Send + Sync + 'static { self.event_store.lock() @@ -963,7 +963,7 @@ impl Client { } } -#[cfg(not(feature = "state"))] +#[cfg(not(feature = "cache"))] impl Client { /// Attaches a handler for when a [`CallDelete`] is received. /// @@ -999,7 +999,7 @@ impl Client { /// /// [`GuildDelete`]: ../model/enum.Event.html#variant.GuildDelete /// [`Role`]: ../model/struct.Role.html - /// [`State`]: ../ext/state/struct.State.html + /// [`Cache`]: ../ext/cache/struct.Cache.html pub fn on_guild_delete(&mut self, handler: F) where F: Fn(Context, Guild) + Send + Sync + 'static { self.event_store.lock() @@ -1010,7 +1010,7 @@ impl Client { /// Attaches a handler for when a [`GuildMemberRemove`] is received. /// /// Returns the user's associated `Member` object, _if_ it existed in the - /// state. + /// cache. /// /// [`GuildMemberRemove`]: ../model/enum.Event.html#variant.GuildMemberRemove pub fn on_guild_member_remove(&mut self, handler: F) @@ -1043,7 +1043,7 @@ impl Client { /// Attaches a handler for when a [`GuildRoleUpdate`] is received. /// /// [`GuildRoleUpdate`]: ../model/enum.Event.html#variant.GuildRoleUpdate - /// [`State`]: ../ext/state/struct.State.html + /// [`Cache`]: ../ext/cache/struct.Cache.html pub fn on_guild_role_update(&mut self, handler: F) where F: Fn(Context, GuildId, Role) + Send + Sync + 'static { self.event_store.lock() diff --git a/src/ext/cache/mod.rs b/src/ext/cache/mod.rs new file mode 100644 index 0000000..484de1f --- /dev/null +++ b/src/ext/cache/mod.rs @@ -0,0 +1,853 @@ +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::default::Default; +use std::mem; +use ::model::*; + +/// 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 +/// [`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. +/// +/// # 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 `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. +/// +/// [`Connection`]: ../../client/struct.Connection.html +/// [`Context`]: ../../client/struct.Context.html +/// [`Context::get_channel`]: ../../client/struct.Context.html#method.get_channel +/// [`LiveGuild`]: ../../model/struct.LiveGuild.html +/// [`Role`]: ../../model/struct.Role.html +/// [`http`]: ../../client/http/index.html +#[derive(Debug, Clone)] +pub struct Cache { + /// A map of the currently active calls that the current user knows about, + /// where the key is the Id of the [`PrivateChannel`] or [`Group`] hosting + /// the call. + /// + /// For bot users this will almost always be empty. + pub calls: HashMap, + /// A map of the groups that the current user is in. + /// + /// For bot users this will almost always be empty. + pub groups: HashMap, + /// Settings specific to a guild. + /// + /// This will always be empty for bot accounts. + pub guild_settings: HashMap, UserGuildSettings>, + pub guilds: HashMap, + /// A list of notes that a user has made for individual users. + /// + /// This will always be empty for bot accounts. + pub notes: HashMap, + pub presences: HashMap, + pub private_channels: HashMap, + pub relationships: HashMap, + /// Account-specific settings for a user account. + pub settings: Option, + pub unavailable_guilds: Vec, + pub user: CurrentUser, +} + +impl Cache { + pub fn unknown_members(&self) -> u64 { + let mut total = 0; + + for guild in self.guilds.values() { + let members = guild.members.len() as u64; + + if guild.member_count > members { + total += guild.member_count - members; + } + } + + total + } + + pub fn all_private_channels(&self) -> Vec { + self.groups + .keys() + .cloned() + .chain(self.private_channels.keys().cloned()) + .collect() + } + + pub fn all_guilds(&self) -> Vec { + self.guilds + .values() + .map(|s| s.id) + .chain(self.unavailable_guilds.iter().cloned()) + .collect() + } + + #[doc(hidden)] + pub fn __download_members(&mut self) -> Vec { + self.guilds + .values_mut() + .filter(|guild| guild.large) + .map(|ref mut guild| { + guild.members.clear(); + + guild.id + }) + .collect::>() + } + + pub fn get_call>(&self, group_id: C) -> Option<&Call> { + self.calls.get(&group_id.into()) + } + + pub fn get_channel>(&self, id: C) -> Option { + let id = id.into(); + + for guild in self.guilds.values() { + for channel in guild.channels.values() { + if channel.id == id { + return Some(Channel::Public(channel.clone())); + } + } + } + + None + } + + pub fn get_guild>(&self, id: G) -> Option<&LiveGuild> { + self.guilds.get(&id.into()) + } + + pub fn get_group>(&self, id: C) -> Option<&Group> { + self.groups.get(&id.into()) + } + + pub fn get_member(&self, guild_id: G, user_id: U) -> Option<&Member> + where G: Into, U: Into { + self.guilds + .get(&guild_id.into()) + .map(|guild| { + guild.members.get(&user_id.into()) + }).and_then(|x| match x { + Some(x) => Some(x), + _ => None, + }) + } + + pub fn get_role(&self, guild_id: G, role_id: R) -> Option<&Role> + where G: Into, R: Into { + if let Some(guild) = self.guilds.get(&guild_id.into()) { + guild.roles.get(&role_id.into()) + } else { + None + } + } + + /// Update the cache according to the changes described in the given event. + #[allow(cyclomatic_complexity)] + #[allow(unneeded_field_pattern)] + pub fn update(&mut self, event: &Event) { + match *event { + Event::CallCreate(ref event) => { + self.update_with_call_create(event); + }, + Event::CallDelete(ref event) => { + self.update_with_call_delete(event); + }, + Event::CallUpdate(ref event) => { + self.update_with_call_update(event, false); + }, + Event::ChannelCreate(ref event) => { + self.update_with_channel_create(event); + }, + Event::ChannelDelete(ref event) => { + self.update_with_channel_delete(event); + }, + Event::ChannelPinsUpdate(ref event) => { + self.update_with_channel_pins_update(event); + }, + Event::ChannelRecipientAdd(ref event) => { + self.update_with_channel_recipient_add(event); + }, + Event::ChannelRecipientRemove(ref event) => { + self.update_with_channel_recipient_remove(event); + }, + Event::ChannelUpdate(ref event) => { + self.update_with_channel_update(event); + }, + Event::GuildCreate(ref event) => { + self.update_with_guild_create(event); + }, + Event::GuildDelete(ref event) => { + self.update_with_guild_delete(event); + }, + Event::GuildEmojisUpdate(ref event) => { + self.update_with_guild_emojis_update(event); + }, + Event::GuildMemberAdd(ref event) => { + self.update_with_guild_member_add(event); + }, + Event::GuildMemberRemove(ref event) => { + self.update_with_guild_member_remove(event); + }, + Event::GuildMemberUpdate(ref event) => { + self.update_with_guild_member_update(event); + }, + Event::GuildMembersChunk(ref event) => { + self.update_with_guild_members_chunk(event); + }, + Event::GuildRoleCreate(ref event) => { + self.update_with_guild_role_create(event); + }, + Event::GuildRoleDelete(ref event) => { + self.update_with_guild_role_delete(event); + }, + Event::GuildRoleUpdate(ref event) => { + self.update_with_guild_role_update(event); + }, + Event::GuildSync(ref event) => { + self.update_with_guild_sync(event); + }, + Event::GuildUnavailable(ref event) => { + self.update_with_guild_unavailable(event); + }, + Event::GuildUpdate(ref event) => { + self.update_with_guild_update(event); + }, + Event::PresencesReplace(ref event) => { + self.update_with_presences_replace(event); + }, + Event::PresenceUpdate(ref event) => { + self.update_with_presence_update(event); + }, + Event::Ready(ref event) => { + self.update_with_ready(event); + }, + Event::RelationshipAdd(ref event) => { + self.update_with_relationship_add(event); + }, + Event::RelationshipRemove(ref event) => { + self.update_with_relationship_remove(event); + }, + Event::UserGuildSettingsUpdate(ref event) => { + self.update_with_user_guild_settings_update(event); + }, + Event::UserNoteUpdate(ref event) => { + self.update_with_user_note_update(event); + }, + Event::UserSettingsUpdate(ref event) => { + self.update_with_user_settings_update(event, false); + }, + Event::UserUpdate(ref event) => { + self.update_with_user_update(event); + }, + Event::VoiceStateUpdate(ref event) => { + self.update_with_voice_state_update(event); + }, + Event::ChannelPinsAck(_) | + Event::FriendSuggestionCreate(_) | + Event::FriendSuggestionDelete(_) | + Event::GuildBanAdd(_) | + Event::GuildBanRemove(_) | + Event::GuildIntegrationsUpdate(_) | + Event::MessageAck(_) | + Event::MessageCreate(_) | + Event::MessageDelete(_) | + Event::MessageDeleteBulk(_) | + Event::MessageUpdate(_) | + Event::ReactionAdd(_) | + Event::ReactionRemove(_) | + Event::ReactionRemoveAll(_) | + Event::Resumed(_) | + Event::TypingStart(_) | + Event::VoiceServerUpdate(_) | + Event::WebhookUpdate(_) | + Event::Unknown(_) => {}, + } + } + + pub fn update_with_call_create(&mut self, event: &CallCreateEvent) { + match self.calls.entry(event.call.channel_id) { + Entry::Vacant(e) => { + e.insert(event.call.clone()); + }, + Entry::Occupied(mut e) => { + e.get_mut().clone_from(&event.call); + }, + } + } + + pub fn update_with_call_delete(&mut self, event: &CallDeleteEvent) + -> Option { + self.calls.remove(&event.channel_id) + } + + pub fn update_with_call_update(&mut self, event: &CallUpdateEvent, old: bool) + -> Option { + let item = if old { + self.calls.get(&event.channel_id).cloned() + } else { + None + }; + + self.calls + .get_mut(&event.channel_id) + .map(|call| { + call.region.clone_from(&event.region); + call.ringing.clone_from(&event.ringing); + }); + + item + } + + pub fn update_with_channel_create(&mut self, event: &ChannelCreateEvent) + -> Option { + match event.channel { + Channel::Group(ref group) => { + let ch = self.groups.insert(group.channel_id, group.clone()); + + ch.map(Channel::Group) + }, + Channel::Private(ref channel) => { + let ch = self.private_channels.insert(channel.id, channel.clone()); + + ch.map(Channel::Private) + }, + Channel::Public(ref channel) => { + let ch = self.guilds + .get_mut(&channel.guild_id) + .map(|guild| { + guild.channels.insert(channel.id, channel.clone()) + }); + + let ch = match ch { + Some(Some(ch)) => Some(ch), + _ => None, + }; + + ch.map(Channel::Public) + }, + } + } + + pub fn update_with_channel_delete(&mut self, event: &ChannelDeleteEvent) + -> Option { + match event.channel { + Channel::Group(ref group) => { + self.groups.remove(&group.channel_id).map(Channel::Group) + }, + Channel::Private(ref channel) => { + self.private_channels.remove(&channel.id) + .map(Channel::Private) + }, + Channel::Public(ref channel) => { + let ch = self.guilds + .get_mut(&channel.guild_id) + .map(|guild| guild.channels.remove(&channel.id)); + + match ch { + Some(Some(ch)) => Some(Channel::Public(ch)), + _ => None, + } + }, + } + } + + pub fn update_with_channel_pins_update(&mut self, + event: &ChannelPinsUpdateEvent) { + if let Some(channel) = self.private_channels.get_mut(&event.channel_id) { + channel.last_pin_timestamp = event.last_pin_timestamp.clone(); + + return; + } + + if let Some(group) = self.groups.get_mut(&event.channel_id) { + group.last_pin_timestamp = event.last_pin_timestamp.clone(); + + return; + } + + // Guild searching is last because it is expensive + // in comparison to private channel and group searching. + for guild in self.guilds.values_mut() { + for channel in guild.channels.values_mut() { + if channel.id == event.channel_id { + channel.last_pin_timestamp = event.last_pin_timestamp.clone(); + + return; + } + } + } + } + + pub fn update_with_channel_recipient_add(&mut self, + event: &ChannelRecipientAddEvent) { + self.groups + .get_mut(&event.channel_id) + .map(|group| group.recipients.insert(event.user.id, + event.user.clone())); + } + + pub fn update_with_channel_recipient_remove(&mut self, + event: &ChannelRecipientRemoveEvent) { + self.groups + .get_mut(&event.channel_id) + .map(|group| group.recipients.remove(&event.user.id)); + } + + pub fn update_with_channel_update(&mut self, event: &ChannelUpdateEvent) { + match event.channel { + Channel::Group(ref group) => { + match self.groups.entry(group.channel_id) { + Entry::Vacant(e) => { + e.insert(group.clone()); + }, + Entry::Occupied(mut e) => { + let dest = e.get_mut(); + + if group.recipients.is_empty() { + let recipients = mem::replace(&mut dest.recipients, HashMap::new()); + + dest.clone_from(group); + + dest.recipients = recipients; + } else { + dest.clone_from(group); + } + }, + } + }, + Channel::Private(ref channel) => { + self.private_channels + .get_mut(&channel.id) + .map(|private| private.clone_from(channel)); + }, + Channel::Public(ref channel) => { + self.guilds + .get_mut(&channel.guild_id) + .map(|guild| guild.channels + .insert(channel.id, channel.clone())); + }, + } + } + + pub fn update_with_guild_create(&mut self, event: &GuildCreateEvent) { + self.guilds.insert(event.guild.id, event.guild.clone()); + } + + pub fn update_with_guild_delete(&mut self, event: &GuildDeleteEvent) + -> Option { + self.guilds.remove(&event.guild.id) + } + + pub fn update_with_guild_emojis_update(&mut self, + event: &GuildEmojisUpdateEvent) { + self.guilds + .get_mut(&event.guild_id) + .map(|guild| guild.emojis.extend(event.emojis.clone())); + } + + pub fn update_with_guild_member_add(&mut self, + event: &GuildMemberAddEvent) { + self.guilds + .get_mut(&event.guild_id) + .map(|guild| { + guild.member_count += 1; + guild.members.insert(event.member.user.id, + event.member.clone()); + }); + } + + pub fn update_with_guild_member_remove(&mut self, + event: &GuildMemberRemoveEvent) + -> Option { + let member = self.guilds + .get_mut(&event.guild_id) + .map(|guild| { + guild.member_count -= 1; + guild.members.remove(&event.user.id) + }); + + match member { + Some(Some(member)) => Some(member), + _ => None, + } + } + + pub fn update_with_guild_member_update(&mut self, + event: &GuildMemberUpdateEvent) + -> Option { + if let Some(guild) = self.guilds.get_mut(&event.guild_id) { + 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.clone_from(&event.user); + + found = true; + + item + } else { + None + }; + + if !found { + guild.members.insert(event.user.id, Member { + deaf: false, + joined_at: String::default(), + mute: false, + nick: event.nick.clone(), + roles: event.roles.clone(), + user: event.user.clone(), + }); + } + + item + } else { + None + } + } + + pub fn update_with_guild_members_chunk(&mut self, + event: &GuildMembersChunkEvent) { + self.guilds + .get_mut(&event.guild_id) + .map(|guild| guild.members.extend(event.members.clone())); + } + + pub fn update_with_guild_role_create(&mut self, + event: &GuildRoleCreateEvent) { + self.guilds + .get_mut(&event.guild_id) + .map(|guild| guild.roles.insert(event.role.id, event.role.clone())); + } + + pub fn update_with_guild_role_delete(&mut self, + event: &GuildRoleDeleteEvent) + -> Option { + let role = self.guilds + .get_mut(&event.guild_id) + .map(|guild| guild.roles.remove(&event.role_id)); + + match role { + Some(Some(x)) => Some(x), + _ => None, + } + } + + pub fn update_with_guild_role_update(&mut self, + event: &GuildRoleUpdateEvent) + -> Option { + let item = self.guilds + .get_mut(&event.guild_id) + .map(|guild| guild.roles + .get_mut(&event.role.id) + .map(|role| mem::replace(role, event.role.clone()))); + + match item { + Some(Some(x)) => Some(x), + _ => None, + } + } + + pub fn update_with_guild_sync(&mut self, event: &GuildSyncEvent) { + self.guilds + .get_mut(&event.guild_id) + .map(|guild| { + guild.large = event.large; + guild.members.clone_from(&event.members); + guild.presences.clone_from(&event.presences); + }); + } + + pub fn update_with_guild_unavailable(&mut self, + event: &GuildUnavailableEvent) { + if !self.unavailable_guilds.contains(&event.guild_id) { + self.unavailable_guilds.push(event.guild_id); + } + } + + pub fn update_with_guild_update(&mut self, event: &GuildUpdateEvent) { + self.guilds + .get_mut(&event.guild.id) + .map(|guild| { + 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; + }); + } + + pub fn update_with_presences_replace(&mut self, event: &PresencesReplaceEvent) { + self.presences.clone_from(&{ + let mut p = HashMap::default(); + + for presence in &event.presences { + p.insert(presence.user_id, presence.clone()); + } + + p + }); + } + + 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) { + // If the user was modified, update the member list + if let Some(user) = event.presence.user.as_ref() { + guild.members + .get_mut(&user.id) + .map(|member| member.user.clone_from(user)); + } + + update_presence(&mut guild.presences, &event.presence); + } + } + } + + pub fn update_with_ready(&mut self, ready: &ReadyEvent) { + let ready = ready.ready.clone(); + + for guild in ready.guilds { + match guild { + PossibleGuild::Offline(guild_id) => { + self.unavailable_guilds.push(guild_id); + } + PossibleGuild::Online(guild) => { + self.guilds.insert(guild.id, guild); + }, + } + } + + self.unavailable_guilds.sort(); + self.unavailable_guilds.dedup(); + + // 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::Public(_) => {}, + } + } + + for guild in ready.user_guild_settings.unwrap_or_default() { + self.guild_settings.insert(guild.guild_id, guild); + } + + for (user_id, presence) in ready.presences { + self.presences.insert(user_id, presence); + } + + for (user_id, relationship) in ready.relationships { + self.relationships.insert(user_id, relationship); + } + + self.notes.extend(ready.notes); + + self.settings = ready.user_settings; + self.user = ready.user; + } + + pub fn update_with_relationship_add(&mut self, event: &RelationshipAddEvent) { + self.relationships.insert(event.relationship.id, + event.relationship.clone()); + } + + pub fn update_with_relationship_remove(&mut self, + event: &RelationshipRemoveEvent) { + self.relationships.remove(&event.user_id); + } + + pub fn update_with_user_guild_settings_update(&mut self, + event: &UserGuildSettingsUpdateEvent) + -> Option { + self.guild_settings + .get_mut(&event.settings.guild_id) + .map(|guild_setting| mem::replace(guild_setting, event.settings.clone())) + } + + pub fn update_with_user_note_update(&mut self, + event: &UserNoteUpdateEvent) + -> Option { + if event.note.is_empty() { + self.notes.remove(&event.user_id) + } else { + self.notes.insert(event.user_id, event.note.clone()) + } + } + + pub fn update_with_user_settings_update(&mut self, + event: &UserSettingsUpdateEvent, + old: bool) + -> Option { + let item = if old { + self.settings.clone() + } else { + None + }; + + self.settings + .as_mut() + .map(|settings| { + opt_modify(&mut settings.enable_tts_command, &event.enable_tts_command); + opt_modify(&mut settings.inline_attachment_media, &event.inline_attachment_media); + opt_modify(&mut settings.inline_embed_media, &event.inline_embed_media); + opt_modify(&mut settings.locale, &event.locale); + opt_modify(&mut settings.message_display_compact, &event.message_display_compact); + opt_modify(&mut settings.render_embeds, &event.render_embeds); + opt_modify(&mut settings.show_current_game, &event.show_current_game); + opt_modify(&mut settings.theme, &event.theme); + opt_modify(&mut settings.convert_emoticons, &event.convert_emoticons); + opt_modify(&mut settings.friend_source_flags, &event.friend_source_flags); + }); + + item + } + + pub fn update_with_user_update(&mut self, event: &UserUpdateEvent) + -> CurrentUser { + mem::replace(&mut self.user, event.current_user.clone()) + } + + 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) { + 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; + } + + if let Some(channel) = event.voice_state.channel_id { + // channel id available, insert voice state + if let Some(call) = self.calls.get_mut(&channel) { + { + let finding = call.voice_states + .get_mut(&event.voice_state.user_id); + + if let Some(grp_state) = finding { + grp_state.clone_from(&event.voice_state); + + return; + } + } + + call.voice_states.insert(event.voice_state.user_id, + event.voice_state.clone()); + } + } else { + // delete this user from any group call containing them + for call in self.calls.values_mut() { + call.voice_states.remove(&event.voice_state.user_id); + } + } + } +} + +impl Default for Cache { + fn default() -> Cache { + Cache { + calls: HashMap::default(), + groups: HashMap::default(), + guild_settings: HashMap::default(), + guilds: HashMap::default(), + notes: HashMap::default(), + presences: HashMap::default(), + private_channels: HashMap::default(), + relationships: HashMap::default(), + settings: None, + unavailable_guilds: Vec::default(), + user: CurrentUser { + avatar: None, + bot: false, + discriminator: 0, + email: None, + id: UserId(0), + mfa_enabled: false, + mobile: None, + name: String::default(), + verified: false, + } + } + } +} + +fn update_presence(presences: &mut HashMap, + presence: &Presence) { + if presence.status == OnlineStatus::Offline { + // Remove the user from the presence list + presences.remove(&presence.user_id); + } else { + // Update or add to the presence list + if let Some(ref mut guild_presence) = presences.get(&presence.user_id) { + if presence.user.is_none() { + guild_presence.clone_from(&presence); + } + + return; + } + presences.insert(presence.user_id, presence.clone()); + } +} + +/// A reference to a private channel, public channel, or group. +#[derive(Debug, Clone, Copy)] +pub enum ChannelRef<'a> { + /// A private channel + Private(&'a PrivateChannel), + /// A group channel + Group(&'a Group), + /// A public channel and its guild + Public(&'a LiveGuild, &'a PublicChannel), +} + +fn opt_modify(dest: &mut T, src: &Option) { + if let Some(val) = src.as_ref() { + dest.clone_from(val); + } +} diff --git a/src/ext/mod.rs b/src/ext/mod.rs index 312074b..4484b0b 100644 --- a/src/ext/mod.rs +++ b/src/ext/mod.rs @@ -6,16 +6,16 @@ //! See each extension's module-level documentation for more information. //! //! Note that the framework module requires the `framework` feature to be -//! enabled (enabled by default), the state requires the `state` feature to be +//! enabled (enabled by default), the cache requires the `cache` feature to be //! enabled (enabled by default), and voice support requires the `voice` feature //! to be enabled (disabled by default). //! //! [`Client`]: ../client/struct.Client.html //! [`Connection`]: ../client/struct.Connection.html +#[cfg(feature = "cache")] +pub mod cache; #[cfg(feature = "framework")] pub mod framework; -#[cfg(feature = "state")] -pub mod state; #[cfg(feature = "voice")] pub mod voice; diff --git a/src/ext/state/mod.rs b/src/ext/state/mod.rs deleted file mode 100644 index 637729c..0000000 --- a/src/ext/state/mod.rs +++ /dev/null @@ -1,853 +0,0 @@ -use std::collections::hash_map::Entry; -use std::collections::HashMap; -use std::default::Default; -use std::mem; -use ::model::*; - -/// A state 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 -/// [`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. -/// -/// # Use by the Context -/// -/// The [`Context`] will automatically attempt to pull from the state for you. -/// For example, the [`Context::get_channel`] method will attempt to find the -/// channel in the state. 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 State. -/// -/// This allows you to only need to perform the `Context::get_channel` call, -/// and not need to first search through the state - 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. -/// -/// [`Connection`]: ../../client/struct.Connection.html -/// [`Context`]: ../../client/struct.Context.html -/// [`Context::get_channel`]: ../../client/struct.Context.html#method.get_channel -/// [`LiveGuild`]: ../../model/struct.LiveGuild.html -/// [`Role`]: ../../model/struct.Role.html -/// [`http`]: ../../client/http/index.html -#[derive(Debug, Clone)] -pub struct State { - /// A map of the currently active calls that the current user knows about, - /// where the key is the Id of the [`PrivateChannel`] or [`Group`] hosting - /// the call. - /// - /// For bot users this will almost always be empty. - pub calls: HashMap, - /// A map of the groups that the current user is in. - /// - /// For bot users this will almost always be empty. - pub groups: HashMap, - /// Settings specific to a guild. - /// - /// This will always be empty for bot accounts. - pub guild_settings: HashMap, UserGuildSettings>, - pub guilds: HashMap, - /// A list of notes that a user has made for individual users. - /// - /// This will always be empty for bot accounts. - pub notes: HashMap, - pub presences: HashMap, - pub private_channels: HashMap, - pub relationships: HashMap, - /// Account-specific settings for a user account. - pub settings: Option, - pub unavailable_guilds: Vec, - pub user: CurrentUser, -} - -impl State { - pub fn unknown_members(&self) -> u64 { - let mut total = 0; - - for guild in self.guilds.values() { - let members = guild.members.len() as u64; - - if guild.member_count > members { - total += guild.member_count - members; - } - } - - total - } - - pub fn all_private_channels(&self) -> Vec { - self.groups - .keys() - .cloned() - .chain(self.private_channels.keys().cloned()) - .collect() - } - - pub fn all_guilds(&self) -> Vec { - self.guilds - .values() - .map(|s| s.id) - .chain(self.unavailable_guilds.iter().cloned()) - .collect() - } - - #[doc(hidden)] - pub fn __download_members(&mut self) -> Vec { - self.guilds - .values_mut() - .filter(|guild| guild.large) - .map(|ref mut guild| { - guild.members.clear(); - - guild.id - }) - .collect::>() - } - - pub fn get_call>(&self, group_id: C) -> Option<&Call> { - self.calls.get(&group_id.into()) - } - - pub fn get_channel>(&self, id: C) -> Option { - let id = id.into(); - - for guild in self.guilds.values() { - for channel in guild.channels.values() { - if channel.id == id { - return Some(Channel::Public(channel.clone())); - } - } - } - - None - } - - pub fn get_guild>(&self, id: G) -> Option<&LiveGuild> { - self.guilds.get(&id.into()) - } - - pub fn get_group>(&self, id: C) -> Option<&Group> { - self.groups.get(&id.into()) - } - - pub fn get_member(&self, guild_id: G, user_id: U) -> Option<&Member> - where G: Into, U: Into { - self.guilds - .get(&guild_id.into()) - .map(|guild| { - guild.members.get(&user_id.into()) - }).and_then(|x| match x { - Some(x) => Some(x), - _ => None, - }) - } - - pub fn get_role(&self, guild_id: G, role_id: R) -> Option<&Role> - where G: Into, R: Into { - if let Some(guild) = self.guilds.get(&guild_id.into()) { - guild.roles.get(&role_id.into()) - } else { - None - } - } - - /// Update the state according to the changes described in the given event. - #[allow(cyclomatic_complexity)] - #[allow(unneeded_field_pattern)] - pub fn update(&mut self, event: &Event) { - match *event { - Event::CallCreate(ref event) => { - self.update_with_call_create(event); - }, - Event::CallDelete(ref event) => { - self.update_with_call_delete(event); - }, - Event::CallUpdate(ref event) => { - self.update_with_call_update(event, false); - }, - Event::ChannelCreate(ref event) => { - self.update_with_channel_create(event); - }, - Event::ChannelDelete(ref event) => { - self.update_with_channel_delete(event); - }, - Event::ChannelPinsUpdate(ref event) => { - self.update_with_channel_pins_update(event); - }, - Event::ChannelRecipientAdd(ref event) => { - self.update_with_channel_recipient_add(event); - }, - Event::ChannelRecipientRemove(ref event) => { - self.update_with_channel_recipient_remove(event); - }, - Event::ChannelUpdate(ref event) => { - self.update_with_channel_update(event); - }, - Event::GuildCreate(ref event) => { - self.update_with_guild_create(event); - }, - Event::GuildDelete(ref event) => { - self.update_with_guild_delete(event); - }, - Event::GuildEmojisUpdate(ref event) => { - self.update_with_guild_emojis_update(event); - }, - Event::GuildMemberAdd(ref event) => { - self.update_with_guild_member_add(event); - }, - Event::GuildMemberRemove(ref event) => { - self.update_with_guild_member_remove(event); - }, - Event::GuildMemberUpdate(ref event) => { - self.update_with_guild_member_update(event); - }, - Event::GuildMembersChunk(ref event) => { - self.update_with_guild_members_chunk(event); - }, - Event::GuildRoleCreate(ref event) => { - self.update_with_guild_role_create(event); - }, - Event::GuildRoleDelete(ref event) => { - self.update_with_guild_role_delete(event); - }, - Event::GuildRoleUpdate(ref event) => { - self.update_with_guild_role_update(event); - }, - Event::GuildSync(ref event) => { - self.update_with_guild_sync(event); - }, - Event::GuildUnavailable(ref event) => { - self.update_with_guild_unavailable(event); - }, - Event::GuildUpdate(ref event) => { - self.update_with_guild_update(event); - }, - Event::PresencesReplace(ref event) => { - self.update_with_presences_replace(event); - }, - Event::PresenceUpdate(ref event) => { - self.update_with_presence_update(event); - }, - Event::Ready(ref event) => { - self.update_with_ready(event); - }, - Event::RelationshipAdd(ref event) => { - self.update_with_relationship_add(event); - }, - Event::RelationshipRemove(ref event) => { - self.update_with_relationship_remove(event); - }, - Event::UserGuildSettingsUpdate(ref event) => { - self.update_with_user_guild_settings_update(event); - }, - Event::UserNoteUpdate(ref event) => { - self.update_with_user_note_update(event); - }, - Event::UserSettingsUpdate(ref event) => { - self.update_with_user_settings_update(event, false); - }, - Event::UserUpdate(ref event) => { - self.update_with_user_update(event); - }, - Event::VoiceStateUpdate(ref event) => { - self.update_with_voice_state_update(event); - }, - Event::ChannelPinsAck(_) | - Event::FriendSuggestionCreate(_) | - Event::FriendSuggestionDelete(_) | - Event::GuildBanAdd(_) | - Event::GuildBanRemove(_) | - Event::GuildIntegrationsUpdate(_) | - Event::MessageAck(_) | - Event::MessageCreate(_) | - Event::MessageDelete(_) | - Event::MessageDeleteBulk(_) | - Event::MessageUpdate(_) | - Event::ReactionAdd(_) | - Event::ReactionRemove(_) | - Event::ReactionRemoveAll(_) | - Event::Resumed(_) | - Event::TypingStart(_) | - Event::VoiceServerUpdate(_) | - Event::WebhookUpdate(_) | - Event::Unknown(_) => {}, - } - } - - pub fn update_with_call_create(&mut self, event: &CallCreateEvent) { - match self.calls.entry(event.call.channel_id) { - Entry::Vacant(e) => { - e.insert(event.call.clone()); - }, - Entry::Occupied(mut e) => { - e.get_mut().clone_from(&event.call); - }, - } - } - - pub fn update_with_call_delete(&mut self, event: &CallDeleteEvent) - -> Option { - self.calls.remove(&event.channel_id) - } - - pub fn update_with_call_update(&mut self, event: &CallUpdateEvent, old: bool) - -> Option { - let item = if old { - self.calls.get(&event.channel_id).cloned() - } else { - None - }; - - self.calls - .get_mut(&event.channel_id) - .map(|call| { - call.region.clone_from(&event.region); - call.ringing.clone_from(&event.ringing); - }); - - item - } - - pub fn update_with_channel_create(&mut self, event: &ChannelCreateEvent) - -> Option { - match event.channel { - Channel::Group(ref group) => { - let ch = self.groups.insert(group.channel_id, group.clone()); - - ch.map(Channel::Group) - }, - Channel::Private(ref channel) => { - let ch = self.private_channels.insert(channel.id, channel.clone()); - - ch.map(Channel::Private) - }, - Channel::Public(ref channel) => { - let ch = self.guilds - .get_mut(&channel.guild_id) - .map(|guild| { - guild.channels.insert(channel.id, channel.clone()) - }); - - let ch = match ch { - Some(Some(ch)) => Some(ch), - _ => None, - }; - - ch.map(Channel::Public) - }, - } - } - - pub fn update_with_channel_delete(&mut self, event: &ChannelDeleteEvent) - -> Option { - match event.channel { - Channel::Group(ref group) => { - self.groups.remove(&group.channel_id).map(Channel::Group) - }, - Channel::Private(ref channel) => { - self.private_channels.remove(&channel.id) - .map(Channel::Private) - }, - Channel::Public(ref channel) => { - let ch = self.guilds - .get_mut(&channel.guild_id) - .map(|guild| guild.channels.remove(&channel.id)); - - match ch { - Some(Some(ch)) => Some(Channel::Public(ch)), - _ => None, - } - }, - } - } - - pub fn update_with_channel_pins_update(&mut self, - event: &ChannelPinsUpdateEvent) { - if let Some(channel) = self.private_channels.get_mut(&event.channel_id) { - channel.last_pin_timestamp = event.last_pin_timestamp.clone(); - - return; - } - - if let Some(group) = self.groups.get_mut(&event.channel_id) { - group.last_pin_timestamp = event.last_pin_timestamp.clone(); - - return; - } - - // Guild searching is last because it is expensive - // in comparison to private channel and group searching. - for guild in self.guilds.values_mut() { - for channel in guild.channels.values_mut() { - if channel.id == event.channel_id { - channel.last_pin_timestamp = event.last_pin_timestamp.clone(); - - return; - } - } - } - } - - pub fn update_with_channel_recipient_add(&mut self, - event: &ChannelRecipientAddEvent) { - self.groups - .get_mut(&event.channel_id) - .map(|group| group.recipients.insert(event.user.id, - event.user.clone())); - } - - pub fn update_with_channel_recipient_remove(&mut self, - event: &ChannelRecipientRemoveEvent) { - self.groups - .get_mut(&event.channel_id) - .map(|group| group.recipients.remove(&event.user.id)); - } - - pub fn update_with_channel_update(&mut self, event: &ChannelUpdateEvent) { - match event.channel { - Channel::Group(ref group) => { - match self.groups.entry(group.channel_id) { - Entry::Vacant(e) => { - e.insert(group.clone()); - }, - Entry::Occupied(mut e) => { - let dest = e.get_mut(); - - if group.recipients.is_empty() { - let recipients = mem::replace(&mut dest.recipients, HashMap::new()); - - dest.clone_from(group); - - dest.recipients = recipients; - } else { - dest.clone_from(group); - } - }, - } - }, - Channel::Private(ref channel) => { - self.private_channels - .get_mut(&channel.id) - .map(|private| private.clone_from(channel)); - }, - Channel::Public(ref channel) => { - self.guilds - .get_mut(&channel.guild_id) - .map(|guild| guild.channels - .insert(channel.id, channel.clone())); - }, - } - } - - pub fn update_with_guild_create(&mut self, event: &GuildCreateEvent) { - self.guilds.insert(event.guild.id, event.guild.clone()); - } - - pub fn update_with_guild_delete(&mut self, event: &GuildDeleteEvent) - -> Option { - self.guilds.remove(&event.guild.id) - } - - pub fn update_with_guild_emojis_update(&mut self, - event: &GuildEmojisUpdateEvent) { - self.guilds - .get_mut(&event.guild_id) - .map(|guild| guild.emojis.extend(event.emojis.clone())); - } - - pub fn update_with_guild_member_add(&mut self, - event: &GuildMemberAddEvent) { - self.guilds - .get_mut(&event.guild_id) - .map(|guild| { - guild.member_count += 1; - guild.members.insert(event.member.user.id, - event.member.clone()); - }); - } - - pub fn update_with_guild_member_remove(&mut self, - event: &GuildMemberRemoveEvent) - -> Option { - let member = self.guilds - .get_mut(&event.guild_id) - .map(|guild| { - guild.member_count -= 1; - guild.members.remove(&event.user.id) - }); - - match member { - Some(Some(member)) => Some(member), - _ => None, - } - } - - pub fn update_with_guild_member_update(&mut self, - event: &GuildMemberUpdateEvent) - -> Option { - if let Some(guild) = self.guilds.get_mut(&event.guild_id) { - 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.clone_from(&event.user); - - found = true; - - item - } else { - None - }; - - if !found { - guild.members.insert(event.user.id, Member { - deaf: false, - joined_at: String::default(), - mute: false, - nick: event.nick.clone(), - roles: event.roles.clone(), - user: event.user.clone(), - }); - } - - item - } else { - None - } - } - - pub fn update_with_guild_members_chunk(&mut self, - event: &GuildMembersChunkEvent) { - self.guilds - .get_mut(&event.guild_id) - .map(|guild| guild.members.extend(event.members.clone())); - } - - pub fn update_with_guild_role_create(&mut self, - event: &GuildRoleCreateEvent) { - self.guilds - .get_mut(&event.guild_id) - .map(|guild| guild.roles.insert(event.role.id, event.role.clone())); - } - - pub fn update_with_guild_role_delete(&mut self, - event: &GuildRoleDeleteEvent) - -> Option { - let role = self.guilds - .get_mut(&event.guild_id) - .map(|guild| guild.roles.remove(&event.role_id)); - - match role { - Some(Some(x)) => Some(x), - _ => None, - } - } - - pub fn update_with_guild_role_update(&mut self, - event: &GuildRoleUpdateEvent) - -> Option { - let item = self.guilds - .get_mut(&event.guild_id) - .map(|guild| guild.roles - .get_mut(&event.role.id) - .map(|role| mem::replace(role, event.role.clone()))); - - match item { - Some(Some(x)) => Some(x), - _ => None, - } - } - - pub fn update_with_guild_sync(&mut self, event: &GuildSyncEvent) { - self.guilds - .get_mut(&event.guild_id) - .map(|guild| { - guild.large = event.large; - guild.members.clone_from(&event.members); - guild.presences.clone_from(&event.presences); - }); - } - - pub fn update_with_guild_unavailable(&mut self, - event: &GuildUnavailableEvent) { - if !self.unavailable_guilds.contains(&event.guild_id) { - self.unavailable_guilds.push(event.guild_id); - } - } - - pub fn update_with_guild_update(&mut self, event: &GuildUpdateEvent) { - self.guilds - .get_mut(&event.guild.id) - .map(|guild| { - 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; - }); - } - - pub fn update_with_presences_replace(&mut self, event: &PresencesReplaceEvent) { - self.presences.clone_from(&{ - let mut p = HashMap::default(); - - for presence in &event.presences { - p.insert(presence.user_id, presence.clone()); - } - - p - }); - } - - 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) { - // If the user was modified, update the member list - if let Some(user) = event.presence.user.as_ref() { - guild.members - .get_mut(&user.id) - .map(|member| member.user.clone_from(user)); - } - - update_presence(&mut guild.presences, &event.presence); - } - } - } - - pub fn update_with_ready(&mut self, ready: &ReadyEvent) { - let ready = ready.ready.clone(); - - for guild in ready.guilds { - match guild { - PossibleGuild::Offline(guild_id) => { - self.unavailable_guilds.push(guild_id); - } - PossibleGuild::Online(guild) => { - self.guilds.insert(guild.id, guild); - }, - } - } - - self.unavailable_guilds.sort(); - self.unavailable_guilds.dedup(); - - // 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::Public(_) => {}, - } - } - - for guild in ready.user_guild_settings.unwrap_or_default() { - self.guild_settings.insert(guild.guild_id, guild); - } - - for (user_id, presence) in ready.presences { - self.presences.insert(user_id, presence); - } - - for (user_id, relationship) in ready.relationships { - self.relationships.insert(user_id, relationship); - } - - self.notes.extend(ready.notes); - - self.settings = ready.user_settings; - self.user = ready.user; - } - - pub fn update_with_relationship_add(&mut self, event: &RelationshipAddEvent) { - self.relationships.insert(event.relationship.id, - event.relationship.clone()); - } - - pub fn update_with_relationship_remove(&mut self, - event: &RelationshipRemoveEvent) { - self.relationships.remove(&event.user_id); - } - - pub fn update_with_user_guild_settings_update(&mut self, - event: &UserGuildSettingsUpdateEvent) - -> Option { - self.guild_settings - .get_mut(&event.settings.guild_id) - .map(|guild_setting| mem::replace(guild_setting, event.settings.clone())) - } - - pub fn update_with_user_note_update(&mut self, - event: &UserNoteUpdateEvent) - -> Option { - if event.note.is_empty() { - self.notes.remove(&event.user_id) - } else { - self.notes.insert(event.user_id, event.note.clone()) - } - } - - pub fn update_with_user_settings_update(&mut self, - event: &UserSettingsUpdateEvent, - old: bool) - -> Option { - let item = if old { - self.settings.clone() - } else { - None - }; - - self.settings - .as_mut() - .map(|settings| { - opt_modify(&mut settings.enable_tts_command, &event.enable_tts_command); - opt_modify(&mut settings.inline_attachment_media, &event.inline_attachment_media); - opt_modify(&mut settings.inline_embed_media, &event.inline_embed_media); - opt_modify(&mut settings.locale, &event.locale); - opt_modify(&mut settings.message_display_compact, &event.message_display_compact); - opt_modify(&mut settings.render_embeds, &event.render_embeds); - opt_modify(&mut settings.show_current_game, &event.show_current_game); - opt_modify(&mut settings.theme, &event.theme); - opt_modify(&mut settings.convert_emoticons, &event.convert_emoticons); - opt_modify(&mut settings.friend_source_flags, &event.friend_source_flags); - }); - - item - } - - pub fn update_with_user_update(&mut self, event: &UserUpdateEvent) - -> CurrentUser { - mem::replace(&mut self.user, event.current_user.clone()) - } - - 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) { - 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; - } - - if let Some(channel) = event.voice_state.channel_id { - // channel id available, insert voice state - if let Some(call) = self.calls.get_mut(&channel) { - { - let finding = call.voice_states - .get_mut(&event.voice_state.user_id); - - if let Some(grp_state) = finding { - grp_state.clone_from(&event.voice_state); - - return; - } - } - - call.voice_states.insert(event.voice_state.user_id, - event.voice_state.clone()); - } - } else { - // delete this user from any group call containing them - for call in self.calls.values_mut() { - call.voice_states.remove(&event.voice_state.user_id); - } - } - } -} - -impl Default for State { - fn default() -> State { - State { - calls: HashMap::default(), - groups: HashMap::default(), - guild_settings: HashMap::default(), - guilds: HashMap::default(), - notes: HashMap::default(), - presences: HashMap::default(), - private_channels: HashMap::default(), - relationships: HashMap::default(), - settings: None, - unavailable_guilds: Vec::default(), - user: CurrentUser { - avatar: None, - bot: false, - discriminator: 0, - email: None, - id: UserId(0), - mfa_enabled: false, - mobile: None, - name: String::default(), - verified: false, - } - } - } -} - -fn update_presence(presences: &mut HashMap, - presence: &Presence) { - if presence.status == OnlineStatus::Offline { - // Remove the user from the presence list - presences.remove(&presence.user_id); - } else { - // Update or add to the presence list - if let Some(ref mut guild_presence) = presences.get(&presence.user_id) { - if presence.user.is_none() { - guild_presence.clone_from(&presence); - } - - return; - } - presences.insert(presence.user_id, presence.clone()); - } -} - -/// A reference to a private channel, public channel, or group. -#[derive(Debug, Clone, Copy)] -pub enum ChannelRef<'a> { - /// A private channel - Private(&'a PrivateChannel), - /// A group channel - Group(&'a Group), - /// A public channel and its guild - Public(&'a LiveGuild, &'a PublicChannel), -} - -fn opt_modify(dest: &mut T, src: &Option) { - if let Some(val) = src.as_ref() { - dest.clone_from(val); - } -} diff --git a/src/ext/voice/connection.rs b/src/ext/voice/connection.rs index 7dfc034..66c8641 100644 --- a/src/ext/voice/connection.rs +++ b/src/ext/voice/connection.rs @@ -14,7 +14,7 @@ use websocket::client::{ Sender as WsSender }; use websocket::stream::WebSocketStream; -use ::client::STATE; +use ::client::CACHE; use ::constants::VoiceOpCode; use ::internal::prelude::*; use ::internal::ws_impl::{ReceiverExt, SenderExt}; @@ -210,7 +210,7 @@ fn identify(info: &ConnectionInfo) -> Value { .insert("server_id", info.server_id) .insert("session_id", &info.session_id) .insert("token", &info.token) - .insert("user_id", STATE.lock().unwrap().user.id.0)) + .insert("user_id", CACHE.lock().unwrap().user.id.0)) .build() } diff --git a/src/lib.rs b/src/lib.rs index 4daa8d9..ef8e8d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,11 +19,11 @@ //! you. See the [Connection's documentation][`Connection`] for more //! information. //! -//! A [`State`] is also provided for you. This will be updated automatically for +//! A [`Cache`] is also provided for you. This will be updated automatically for //! you as data is received from the Discord API via events. When calling a -//! method on a [`Context`], the state will first be searched for relevant data +//! method on a [`Context`], the cache will first be searched for relevant data //! to avoid unnecessary HTTP requests to the Discord API. For more information, -//! see the [state's module-level documentation][state docs]. +//! see the [cache's module-level documentation][cache docs]. //! //! Note that - although this documentation will try to be as up-to-date and //! accurate as possible - Discord hosts [official documentation][docs]. If you @@ -69,11 +69,11 @@ //! [`Context`]: client/struct.Context.html //! [`Event`]: model/enum.Event.html //! [`Event::MessageCreate`]: model/enum.Event.html#variant.MessageCreate -//! [`State`]: ext/state/struct.State.html +//! [`Cache`]: ext/cache/struct.Cache.html //! [client's module-level documentation]: client/index.html //! [docs]: https://discordapp.com/developers/docs/intro //! [examples]: https://github.com/zeyla/serenity.rs/tree/master/examples -//! [state docs]: ext/state/index.html +//! [cache docs]: ext/cache/index.html #![allow(doc_markdown, inline_always, unknown_lints)] #![warn(dead_code, enum_glob_use, if_not_else)] diff --git a/src/model/channel.rs b/src/model/channel.rs index ddbd565..0513966 100644 --- a/src/model/channel.rs +++ b/src/model/channel.rs @@ -31,7 +31,7 @@ use super::utils; #[cfg(feature = "methods")] use ::utils::builder::{CreateEmbed, CreateInvite, EditChannel}; #[cfg(feature = "methods")] -use ::client::{STATE, http}; +use ::client::{CACHE, http}; impl Attachment { /// If this attachment is an image, then a tuple of the width and height @@ -436,7 +436,7 @@ impl Message { #[cfg(feature = "methods")] pub fn delete(&self) -> Result<()> { let req = permissions::MANAGE_MESSAGES; - let is_author = self.author.id != STATE.lock().unwrap().user.id; + let is_author = self.author.id != CACHE.lock().unwrap().user.id; if is_author { return Err(Error::Client(ClientError::InvalidUser)); @@ -503,7 +503,7 @@ impl Message { return Err(Error::Client(ClientError::MessageTooLong(length_over))); } - if self.author.id != STATE.lock().unwrap().user.id { + if self.author.id != CACHE.lock().unwrap().user.id { return Err(Error::Client(ClientError::InvalidUser)); } @@ -720,7 +720,7 @@ impl PrivateChannel { /// [`ClientError::InvalidUser`]: ../client/enum.ClientError.html#variant.InvalidOperationAsUser #[cfg(feature = "methods")] pub fn delete_messages(&self, message_ids: &[MessageId]) -> Result<()> { - if !STATE.lock().unwrap().user.bot { + if !CACHE.lock().unwrap().user.bot { return Err(Error::Client(ClientError::InvalidOperationAsUser)); } @@ -883,13 +883,13 @@ impl PublicChannel { } } - /// Attempts to find this channel's guild in the State. + /// Attempts to find this channel's guild in the Cache. /// /// **Note**: Right now this performs a clone of the guild. This will be /// optimized in the future. - #[cfg(all(feature = "methods", feature = "state"))] + #[cfg(all(feature = "cache", feature = "methods"))] pub fn guild(&self) -> Option { - STATE.lock().unwrap().get_guild(self.guild_id).cloned() + CACHE.lock().unwrap().get_guild(self.guild_id).cloned() } /// Return a [`Mention`] which will link to this channel. @@ -978,7 +978,7 @@ impl Reaction { /// [permissions]: permissions #[cfg(feature = "methods")] pub fn delete(&self) -> Result<()> { - let user = if self.user_id == STATE.lock().unwrap().user.id { + let user = if self.user_id == CACHE.lock().unwrap().user.id { None } else { Some(self.user_id.0) diff --git a/src/model/gateway.rs b/src/model/gateway.rs index 5fdfdfe..113bb2f 100644 --- a/src/model/gateway.rs +++ b/src/model/gateway.rs @@ -239,7 +239,7 @@ pub struct ReactionRemoveAllEvent { pub message_id: MessageId, } -/// The "Ready" event, containing initial state +/// The "Ready" event, containing initial ready cache #[derive(Clone, Debug)] pub struct ReadyEvent { pub ready: Ready, @@ -440,7 +440,7 @@ pub enum Event { /// [`Reaction`]: struct.Reaction.html /// [`on_reaction_remove_all`]: ../client/struct.Clint.html#method.on_reaction_remove_all ReactionRemoveAll(ReactionRemoveAllEvent), - /// The first event in a connection, containing the initial state. + /// The first event in a connection, containing the initial ready cache. /// /// May also be received at a later time in the event of a reconnect. Ready(ReadyEvent), diff --git a/src/model/guild.rs b/src/model/guild.rs index 1c4ffe4..704a93b 100644 --- a/src/model/guild.rs +++ b/src/model/guild.rs @@ -25,8 +25,8 @@ use ::utils::builder::{EditGuild, EditMember, EditRole}; #[cfg(feature = "methods")] use ::client::http; -#[cfg(feature = "state")] -use ::client::STATE; +#[cfg(feature = "cache")] +use ::client::CACHE; impl From for GuildContainer { fn from(guild: Guild) -> GuildContainer { @@ -47,12 +47,12 @@ impl From for GuildContainer { } impl Emoji { - /// Finds the [`Guild`] that owns the emoji by looking through the state. + /// Finds the [`Guild`] that owns the emoji by looking through the Cache. /// /// [`Guild`]: struct.Guild.html #[cfg(feature = "methods")] pub fn find_guild_id(&self) -> Option { - STATE.lock() + CACHE.lock() .unwrap() .guilds .values() @@ -165,9 +165,9 @@ impl Guild { } impl LiveGuild { - #[cfg(feature = "state")] + #[cfg(feature = "cache")] fn has_perms(&self, mut permissions: Permissions) -> Result { - let member = match self.get_member(STATE.lock().unwrap().user.id) { + let member = match self.get_member(CACHE.lock().unwrap().user.id) { Some(member) => member, None => return Err(Error::Client(ClientError::ItemMissing)), }; @@ -179,7 +179,7 @@ impl LiveGuild { Ok(permissions.is_empty()) } - #[cfg(not(feature = "state"))] + #[cfg(not(feature = "cache"))] fn has_perms(&self, mut permissions: Permissions) -> Result { Ok(true) } @@ -379,7 +379,7 @@ impl LiveGuild { /// [`ClientError::InvalidUser`]: ../client/enum.ClientError.html#variant.InvalidUser #[cfg(feature = "methods")] pub fn delete(&self) -> Result { - if self.owner_id != STATE.lock().unwrap().user.id { + if self.owner_id != CACHE.lock().unwrap().user.id { let req = permissions::MANAGE_GUILD; return Err(Error::Client(ClientError::InvalidPermissions(req))); @@ -856,7 +856,7 @@ impl Member { /// [`Guild`]: struct.Guild.html #[cfg(feature = "methods")] pub fn find_guild(&self) -> Result { - STATE.lock() + CACHE.lock() .unwrap() .guilds .values() @@ -926,12 +926,12 @@ impl Member { /// Retrieves the full role data for the user's roles. /// - /// This is shorthand for manually searching through the state. + /// This is shorthand for manually searching through the CACHE. /// /// If role data can not be found for the member, then `None` is returned. - #[cfg(all(feature = "methods", feature = "state"))] + #[cfg(all(feature = "cache", feature = "methods"))] pub fn roles(&self) -> Option> { - STATE.lock().unwrap() + CACHE.lock().unwrap() .guilds .values() .find(|g| g.members @@ -1018,17 +1018,17 @@ impl Role { http::delete_role(guild_id.0, self.id.0) } - /// Searches the state for the guild that owns the role. + /// Searches the cache for the guild that owns the role. /// /// # Errors /// - /// Returns a [`ClientError::GuildNotFound`] if a guild is not in the state + /// Returns a [`ClientError::GuildNotFound`] if a guild is not in the cache /// that contains the role. /// /// [`ClientError::GuildNotFound`]: ../client/enum.ClientError.html#variant.GuildNotFound #[cfg(feature = "methods")] pub fn find_guild(&self) -> Result { - STATE.lock() + CACHE.lock() .unwrap() .guilds .values() diff --git a/src/model/id.rs b/src/model/id.rs index 8c94dec..f055784 100644 --- a/src/model/id.rs +++ b/src/model/id.rs @@ -1,22 +1,22 @@ use super::*; #[cfg(feature = "methods")] -use ::client::{STATE, http}; +use ::client::{CACHE, http}; #[cfg(feature = "methods")] use ::internal::prelude::*; impl ChannelId { - /// Search the state for the channel with the Id. + /// Search the cache for the channel with the Id. #[cfg(feature="methods")] pub fn find(&self) -> Option { - STATE.lock().unwrap().get_channel(*self) + CACHE.lock().unwrap().get_channel(*self) } - /// Search the state for the channel. If it can't be found, the channel is + /// Search the cache for the channel. If it can't be found, the channel is /// requested over REST. #[cfg(feature="methods")] pub fn get(&self) -> Result { - if let Some(channel) = STATE.lock().unwrap().get_channel(*self) { + if let Some(channel) = CACHE.lock().unwrap().get_channel(*self) { return Ok(channel.clone()); } @@ -74,10 +74,10 @@ impl From for EmojiId { } impl GuildId { - /// Search the state for the guild. + /// Search the cache for the guild. #[cfg(feature="methods")] pub fn find(&self) -> Option { - STATE.lock().unwrap().get_guild(*self).cloned() + CACHE.lock().unwrap().get_guild(*self).cloned() } /// Requests the guild over REST. @@ -160,10 +160,10 @@ impl From for RoleId { } impl RoleId { - /// Search the state for the role. + /// Search the cache for the role. #[cfg(feature="methods")] pub fn find(&self) -> Option { - STATE.lock() + CACHE.lock() .unwrap() .guilds .values() diff --git a/src/model/invite.rs b/src/model/invite.rs index 58773d1..6552783 100644 --- a/src/model/invite.rs +++ b/src/model/invite.rs @@ -3,8 +3,8 @@ use ::client::http; use ::internal::prelude::*; use super::{permissions, utils}; -#[cfg(feature = "state")] -use ::client::STATE; +#[cfg(feature = "cache")] +use ::client::CACHE; impl Invite { /// Accepts the invite, placing the current user in the [`Guild`] that the @@ -19,7 +19,7 @@ impl Invite { /// /// # Errors /// - /// If the `state` features is enabled, then this returns a + /// If the `cache` features is enabled, then this returns a /// [`ClientError::InvalidOperationAsBot`] if the current user does not have /// the required [permission]. /// @@ -29,8 +29,8 @@ impl Invite { /// [permission]: permissions/index.html #[cfg(feature="methods")] pub fn accept(&self) -> Result { - feature_state_enabled! {{ - if STATE.lock().unwrap().user.bot { + feature_cache_enabled! {{ + if CACHE.lock().unwrap().user.bot { return Err(Error::Client(ClientError::InvalidOperationAsBot)); } }} @@ -83,8 +83,8 @@ impl RichInvite { /// [`http::accept_invite`]: ../client/http/fn.accept_invite.html #[cfg(feature="methods")] pub fn accept(&self) -> Result { - feature_state_enabled! {{ - if STATE.lock().unwrap().user.bot { + feature_cache_enabled! {{ + if CACHE.lock().unwrap().user.bot { return Err(Error::Client(ClientError::InvalidOperationAsBot)); } }} @@ -100,7 +100,7 @@ impl RichInvite { /// /// # Errors /// - /// If the `state` feature is enabled, then this returns a + /// If the `cache` feature is enabled, then this returns a /// [`ClientError::InvalidPermissions`] if the current user does not have /// the required [permission]. /// diff --git a/src/model/mod.rs b/src/model/mod.rs index 26041a9..7f8a6ea 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -138,11 +138,11 @@ pub enum Channel { /// A container for guilds. /// /// This is used to differentiate whether a guild itself can be used or whether -/// a guild needs to be retrieved from the state. +/// a guild needs to be retrieved from the cache. pub enum GuildContainer { /// A guild which can have its contents directly searched. Guild(Guild), - /// A guild's id, which can be used to search the state for a guild. + /// A guild's id, which can be used to search the cache for a guild. Id(GuildId), } diff --git a/src/model/user.rs b/src/model/user.rs index 412d1ae..9e4c457 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -22,8 +22,8 @@ use time::Timespec; #[cfg(feature = "methods")] use ::client::http; -#[cfg(feature = "state")] -use ::client::STATE; +#[cfg(feature = "cache")] +use ::client::CACHE; impl CurrentUser { /// Returns the formatted URL of the user's icon, if one exists. @@ -56,13 +56,13 @@ impl User { } /// Send a direct message to a user. This will create or retrieve the - /// PrivateChannel over REST if one is not already in the State, and then + /// PrivateChannel over REST if one is not already in the cache, and then /// send a message to it. #[cfg(feature="methods")] pub fn direct_message(&self, content: &str) -> Result { let private_channel_id = { - let finding = STATE.lock() + let finding = CACHE.lock() .unwrap() .private_channels .values() @@ -90,11 +90,11 @@ impl User { } /// Check if a user has a [`Role`]. This will retrieve the - /// [`Guild`] from the [`State`] if - /// it is available, and then check if that guild has the given [`Role`]. + /// [`Guild`] from the [`Cache`] if it is available, and then check if that + /// guild has the given [`Role`]. /// /// If the [`Guild`] is not present, then the guild will be retrieved from - /// the API and the state will be updated with it. + /// the API and the cache will be updated with it. /// /// If there are issues with requesting the API, then `false` will be /// returned. @@ -114,7 +114,7 @@ impl User { /// [`Guild`]: struct.Guild.html /// [`GuildId`]: struct.GuildId.html /// [`Role`]: struct.Role.html - /// [`State`]: ../ext/state/struct.State.html + /// [`Cache`]: ../ext/cache/struct.Cache.html pub fn has_role(&self, guild: G, role: R) -> bool where G: Into, R: Into { let role_id = role.into(); @@ -124,10 +124,10 @@ impl User { guild.roles.get(&role_id).is_some() }, GuildContainer::Id(guild_id) => { - feature_state! {{ - let state = STATE.lock().unwrap(); + feature_cache! {{ + let cache = CACHE.lock().unwrap(); - state.get_role(guild_id, role_id).is_some() + cache.get_role(guild_id, role_id).is_some() } else { true }} diff --git a/src/model/utils.rs b/src/model/utils.rs index db39939..60fa892 100644 --- a/src/model/utils.rs +++ b/src/model/utils.rs @@ -19,8 +19,8 @@ use ::utils::{decode_array, into_array}; #[cfg(feature = "methods")] use super::permissions::{self, Permissions}; -#[cfg(feature = "methods")] -use ::client::STATE; +#[cfg(all(feature = "cache", feature = "methods"))] +use ::client::CACHE; #[macro_escape] macro_rules! missing { @@ -271,14 +271,14 @@ pub fn remove(map: &mut BTreeMap, key: &str) -> Result { } #[doc(hidden)] -#[cfg(feature="methods")] +#[cfg(all(feature = "cache", feature="methods"))] pub fn user_has_perms(channel_id: ChannelId, mut permissions: Permissions) -> Result { - let state = STATE.lock().unwrap(); - let current_user = &state.user; + let cache = CACHE.lock().unwrap(); + let current_user = &cache.user; - let channel = match state.get_channel(channel_id) { + let channel = match cache.get_channel(channel_id) { Some(channel) => channel, None => return Err(Error::Client(ClientError::ItemMissing)), }; @@ -290,7 +290,7 @@ pub fn user_has_perms(channel_id: ChannelId, Channel::Public(channel) => channel.guild_id, }; - let guild = match state.get_guild(guild_id) { + let guild = match cache.get_guild(guild_id) { Some(guild) => guild, None => return Err(Error::Client(ClientError::ItemMissing)), }; diff --git a/src/utils/macros.rs b/src/utils/macros.rs index cdcbbf2..4a1d93b 100644 --- a/src/utils/macros.rs +++ b/src/utils/macros.rs @@ -48,6 +48,53 @@ macro_rules! status_concat { } } +// Enable/disable check for cache +#[cfg(feature = "cache")] +macro_rules! feature_cache { + ($enabled:block else $disabled:block) => { + { + $enabled + } + } +} + +#[cfg(not(feature = "cache"))] +macro_rules! feature_cache { + ($enabled:block else $disabled:block) => { + { + $disabled + } + } +} + +#[cfg(feature = "cache")] +macro_rules! feature_cache_enabled { + ($enabled:block) => { + { + $enabled + } + } +} + +#[cfg(not(feature = "cache"))] +macro_rules! feature_cache_enabled { + ($enabled:block) => {{}} +} + +#[cfg(feature = "cache")] +macro_rules! feature_cache_disabled { + ($disabled:block) => {{}} +} + +#[cfg(not(feature = "cache"))] +macro_rules! feature_cache_disabled { + ($disabled:block) => { + { + $disabled + } + } +} + // Enable/disable check for extras #[cfg(feature = "extras")] macro_rules! feature_extras { @@ -191,53 +238,6 @@ macro_rules! feature_methods_disabled { } } -// Enable/disable check for state -#[cfg(feature = "state")] -macro_rules! feature_state { - ($enabled:block else $disabled:block) => { - { - $enabled - } - } -} - -#[cfg(not(feature = "state"))] -macro_rules! feature_state { - ($enabled:block else $disabled:block) => { - { - $disabled - } - } -} - -#[cfg(feature = "state")] -macro_rules! feature_state_enabled { - ($enabled:block) => { - { - $enabled - } - } -} - -#[cfg(not(feature = "state"))] -macro_rules! feature_state_enabled { - ($enabled:block) => {{}} -} - -#[cfg(feature = "state")] -macro_rules! feature_state_disabled { - ($disabled:block) => {{}} -} - -#[cfg(not(feature = "state"))] -macro_rules! feature_state_disabled { - ($disabled:block) => { - { - $disabled - } - } -} - // Enable/disable check for voice #[cfg(feature = "voice")] macro_rules! feature_voice { -- cgit v1.2.3