aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLakelezz <[email protected]>2018-12-18 20:55:32 +0100
committerAlex M. M <[email protected]>2018-12-18 20:55:32 +0100
commit8cb1bdc6cf992cc55810f5af753666d54f2237d5 (patch)
tree052e2f425a6117e45323886bb2b2b683b6d5a1e8 /src
parentMutably borrow on `ChannelId`'s `edit`-method. (#447) (diff)
downloadserenity-8cb1bdc6cf992cc55810f5af753666d54f2237d5.tar.xz
serenity-8cb1bdc6cf992cc55810f5af753666d54f2237d5.zip
Remove global Cache (#448)
* Update to use Rust 2018. * Update examples and use Rust 2018. * Pass cache via `Context` around instead of being global. * Remove `lazy_static` from `cache`-feature. * Update examples to use `Context`'s cache. * Replace cache's update-timeout-setting with `update_cache_timeout`. * Update documentation to stop using global cache. * Move `HttpAndCache` to `lib.rs`. * Add `__nonexhaustive`-field to `CacheAndHttp`. * Add `__nonexhaustive` in `CacheAndHttp`-initialisers. * Avoid `__nonexhaustive`-usage in doctest. * Remove unnecessary comma in `cfg`-attribute.
Diffstat (limited to 'src')
-rw-r--r--src/builder/create_embed.rs7
-rw-r--r--src/builder/create_invite.rs75
-rw-r--r--src/builder/edit_message.rs7
-rw-r--r--src/cache/mod.rs156
-rw-r--r--src/cache/settings.rs54
-rw-r--r--src/client/bridge/gateway/shard_manager.rs10
-rw-r--r--src/client/bridge/gateway/shard_queuer.rs3
-rw-r--r--src/client/bridge/gateway/shard_runner.rs7
-rw-r--r--src/client/context.rs12
-rw-r--r--src/client/dispatch.rs191
-rw-r--r--src/client/mod.rs120
-rw-r--r--src/framework/standard/command.rs2
-rw-r--r--src/framework/standard/help_commands.rs54
-rw-r--r--src/framework/standard/mod.rs26
-rw-r--r--src/lib.rs52
-rw-r--r--src/model/channel/channel_category.rs18
-rw-r--r--src/model/channel/channel_id.rs25
-rw-r--r--src/model/channel/guild_channel.rs94
-rw-r--r--src/model/channel/message.rs78
-rw-r--r--src/model/channel/mod.rs60
-rw-r--r--src/model/channel/reaction.rs37
-rw-r--r--src/model/guild/emoji.rs55
-rw-r--r--src/model/guild/guild_id.rs20
-rw-r--r--src/model/guild/member.rs36
-rw-r--r--src/model/guild/mod.rs91
-rw-r--r--src/model/guild/partial_guild.rs12
-rw-r--r--src/model/guild/role.rs32
-rw-r--r--src/model/invite.rs26
-rw-r--r--src/model/misc.rs14
-rw-r--r--src/model/user.rs140
-rw-r--r--src/model/utils.rs6
-rw-r--r--src/utils/mod.rs64
32 files changed, 863 insertions, 721 deletions
diff --git a/src/builder/create_embed.rs b/src/builder/create_embed.rs
index 5c82bb5..f075fcf 100644
--- a/src/builder/create_embed.rs
+++ b/src/builder/create_embed.rs
@@ -267,9 +267,8 @@ impl CreateEmbed {
/// struct Handler;
///
/// impl EventHandler for Handler {
- /// fn guild_member_addition(&self, _: Context, guild_id: GuildId, member: Member) {
- /// use serenity::CACHE;
- /// let cache = CACHE.read();
+ /// fn guild_member_addition(&self, context: Context, guild_id: GuildId, member: Member) {
+ /// let cache = context.cache.read();
///
/// if let Some(guild) = cache.guild(guild_id) {
/// let guild = guild.read();
@@ -282,7 +281,7 @@ impl CreateEmbed {
/// if let Some(channel) = channel_search {
/// let user = member.user.read();
///
- /// let _ = channel.write().send_message(|m| {
+ /// let _ = channel.write().send_message(&context, |m| {
/// m.embed(|e| {
/// e.author(|a| {
/// a.icon_url(&user.face()).name(&user.name)
diff --git a/src/builder/create_invite.rs b/src/builder/create_invite.rs
index 99c2bb5..4858a8e 100644
--- a/src/builder/create_invite.rs
+++ b/src/builder/create_invite.rs
@@ -19,10 +19,9 @@ use crate::utils::VecMap;
/// struct Handler;
///
/// impl EventHandler for Handler {
-/// fn message(&self, _: Context, msg: Message) {
-/// use serenity::CACHE;
+/// fn message(&self, context: Context, msg: Message) {
/// if msg.content == "!createinvite" {
-/// let channel = match CACHE.read().guild_channel(msg.channel_id) {
+/// let channel = match context.cache.read().guild_channel(msg.channel_id) {
/// Some(channel) => channel,
/// None => {
/// let _ = msg.channel_id.say("Error creating invite");
@@ -33,7 +32,7 @@ use crate::utils::VecMap;
///
/// let reader = channel.read();
///
-/// let creation = reader.create_invite(|i| {
+/// let creation = reader.create_invite(&context, |i| {
/// i.max_age(3600).max_uses(10)
/// });
///
@@ -80,23 +79,17 @@ impl CreateInvite {
/// Create an invite with a max age of `3600` seconds, or 1 hour:
///
/// ```rust,no_run
- /// # use serenity::CACHE;
- /// # use serenity::model::id::ChannelId;
- /// # use std::error::Error;
+ /// # use serenity::{command, model::id::ChannelId};
+ /// # use std::{error::Error, sync::Arc};
/// #
- /// # fn try_main() -> Result<(), Box<Error>> {
- /// # let channel = CACHE.read().guild_channel(81384788765712384).unwrap();
+ /// # command!(example(context) {
+ /// # let channel = context.cache.read().guild_channel(81384788765712384).unwrap();
/// # let channel = channel.read();
/// #
- /// let invite = channel.create_invite(|i| {
+ /// let invite = channel.create_invite(&context, |i| {
/// i.max_age(3600)
/// })?;
- /// # Ok(())
- /// # }
- /// #
- /// # fn main() {
- /// # try_main().unwrap();
- /// # }
+ /// # });
/// ```
pub fn max_age(&mut self, max_age: u64) -> &mut Self {
self.0.insert("max_age", Value::Number(Number::from(max_age)));
@@ -114,23 +107,16 @@ impl CreateInvite {
/// Create an invite with a max use limit of `5`:
///
/// ```rust,no_run
- /// # use serenity::CACHE;
- /// # use serenity::model::id::ChannelId;
- /// # use std::error::Error;
+ /// # use serenity::{command, model::id::ChannelId};
/// #
- /// # fn try_main() -> Result<(), Box<Error>> {
- /// # let channel = CACHE.read().guild_channel(81384788765712384).unwrap();
+ /// # command!(example(context) {
+ /// # let channel = context.cache.read().guild_channel(81384788765712384).unwrap();
/// # let channel = channel.read();
/// #
- /// let invite = channel.create_invite(|i| {
+ /// let invite = channel.create_invite(&context, |i| {
/// i.max_uses(5)
/// })?;
- /// # Ok(())
- /// # }
- /// #
- /// # fn main() {
- /// # try_main().unwrap();
- /// # }
+ /// # });
/// ```
pub fn max_uses(&mut self, max_uses: u64) -> &mut Self {
self.0.insert("max_uses", Value::Number(Number::from(max_uses)));
@@ -146,23 +132,17 @@ impl CreateInvite {
/// Create an invite which is temporary:
///
/// ```rust,no_run
- /// # use serenity::CACHE;
- /// # use serenity::model::id::ChannelId;
+ /// # use serenity::{command, model::id::ChannelId};
/// # use std::error::Error;
/// #
- /// # fn try_main() -> Result<(), Box<Error>> {
- /// # let channel = CACHE.read().guild_channel(81384788765712384).unwrap();
+ /// # command!(example(context) {
+ /// # let channel = context.cache.read().guild_channel(81384788765712384).unwrap();
/// # let channel = channel.read();
/// #
- /// let invite = channel.create_invite(|i| {
+ /// let invite = channel.create_invite(&context, |i| {
/// i.temporary(true)
/// })?;
- /// # Ok(())
- /// # }
- /// #
- /// # fn main() {
- /// # try_main().unwrap();
- /// # }
+ /// # });
/// ```
pub fn temporary(&mut self, temporary: bool) -> &mut Self {
self.0.insert("temporary", Value::Bool(temporary));
@@ -178,23 +158,16 @@ impl CreateInvite {
/// Create an invite which is unique:
///
/// ```rust,no_run
- /// # use serenity::CACHE;
- /// # use serenity::model::id::ChannelId;
- /// # use std::error::Error;
+ /// # use serenity::{command, Error, model::id::ChannelId};
/// #
- /// # fn try_main() -> Result<(), Box<Error>> {
- /// # let channel = CACHE.read().guild_channel(81384788765712384).unwrap();
+ /// # command!(example(context) {
+ /// # let channel = context.cache.read().guild_channel(81384788765712384).unwrap();
/// # let channel = channel.read();
/// #
- /// let invite = channel.create_invite(|i| {
+ /// let invite = channel.create_invite(&context, |i| {
/// i.unique(true)
/// })?;
- /// # Ok(())
- /// # }
- /// #
- /// # fn main() {
- /// # try_main().unwrap();
- /// # }
+ /// # });
/// ```
pub fn unique(&mut self, unique: bool) -> &mut Self {
self.0.insert("unique", Value::Bool(unique));
diff --git a/src/builder/edit_message.rs b/src/builder/edit_message.rs
index 2410a13..8c1cd8f 100644
--- a/src/builder/edit_message.rs
+++ b/src/builder/edit_message.rs
@@ -10,13 +10,14 @@ use crate::utils::{self, VecMap};
/// Editing the content of a [`Message`] to `"hello"`:
///
/// ```rust,no_run
-/// # use serenity::model::id::{ChannelId, MessageId};
+/// # use serenity::{command, model::id::{ChannelId, MessageId}};
/// #
+/// # command!(example(context) {
/// # let mut message = ChannelId(7).message(MessageId(8)).unwrap();
-/// #
-/// let _ = message.edit(|m| {
+/// let _ = message.edit(&context, |m| {
/// m.content("hello")
/// });
+/// # });
/// ```
///
/// [`Message`]: ../model/channel/struct.Message.html
diff --git a/src/cache/mod.rs b/src/cache/mod.rs
index a2c133f..6dbf097 100644
--- a/src/cache/mod.rs
+++ b/src/cache/mod.rs
@@ -42,6 +42,7 @@
//! [`CACHE`]: ../struct.CACHE.html
//! [`http`]: ../http/index.html
+use std::str::FromStr;
use crate::model::prelude::*;
use parking_lot::RwLock;
use std::collections::{
@@ -53,7 +54,6 @@ use std::collections::{
use std::{
default::Default,
sync::Arc,
- time::Duration,
};
mod cache_update;
@@ -64,6 +64,30 @@ pub use self::settings::Settings;
type MessageCache = HashMap<ChannelId, HashMap<MessageId, Message>>;
+pub trait FromStrAndCache: Sized {
+ type Err;
+
+ fn from_str(cache: &Arc<RwLock<Cache>>, s: &str) -> Result<Self, Self::Err>;
+}
+
+pub trait StrExt: Sized {
+ fn parse_cached<F: FromStrAndCache>(&self, cache: &Arc<RwLock<Cache>>) -> Result<F, F::Err>;
+}
+
+impl<'a> StrExt for &'a str {
+ fn parse_cached<F: FromStrAndCache>(&self, cache: &Arc<RwLock<Cache>>) -> Result<F, F::Err> {
+ F::from_str(&cache, &self)
+ }
+}
+
+impl<F: FromStr> FromStrAndCache for F {
+ type Err = F::Err;
+
+ fn from_str(_cache: &Arc<RwLock<Cache>>, s: &str) -> Result<Self, Self::Err> {
+ s.parse::<F>()
+ }
+}
+
/// A cache of all events received over a [`Shard`], where storing at least
/// some data from the event is possible.
///
@@ -222,7 +246,6 @@ impl Cache {
/// #
/// # #[cfg(feature = "client")]
/// # fn main() {
- /// use serenity::CACHE;
/// use std::thread;
/// use std::time::Duration;
///
@@ -241,7 +264,7 @@ impl Cache {
/// // seconds.
/// thread::sleep(Duration::from_secs(5));
///
- /// println!("{} unknown members", CACHE.read().unknown_members());
+ /// println!("{} unknown members", ctx.cache.read().unknown_members());
/// }
/// }
///
@@ -284,9 +307,15 @@ impl Cache {
/// Printing the count of all private channels and groups:
///
/// ```rust,no_run
- /// use serenity::CACHE;
- ///
- /// let amount = CACHE.read().all_private_channels().len();
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
+ /// #
+ /// # use serenity::{cache::Cache};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
+ /// #
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// let amount = cache.read().all_private_channels().len();
///
/// println!("There are {} private channels", amount);
/// ```
@@ -316,13 +345,11 @@ impl Cache {
/// # use serenity::model::prelude::*;
/// # use serenity::prelude::*;
/// #
- /// use serenity::CACHE;
- ///
/// struct Handler;
///
/// impl EventHandler for Handler {
- /// fn ready(&self, _: Context, _: Ready) {
- /// let guilds = CACHE.read().guilds.len();
+ /// fn ready(&self, context: Context, _: Ready) {
+ /// let guilds = context.cache.read().guilds.len();
///
/// println!("Guilds in the Cache: {}", guilds);
/// }
@@ -397,20 +424,21 @@ impl Cache {
/// Retrieve a guild from the cache and print its name:
///
/// ```rust,no_run
- /// # use std::error::Error;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
/// #
- /// # fn try_main() -> Result<(), Box<Error>> {
- /// use serenity::CACHE;
- ///
- /// if let Some(guild) = CACHE.read().guild(7) {
+ /// # use serenity::{cache::Cache};
+ /// # use parking_lot::RwLock;
+ /// # use std::{error::Error, sync::Arc};
+ /// #
+ /// # fn main() -> Result<(), Box<Error>> {
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// // assuming the cache is in scope, e.g. via `Context`
+ /// if let Some(guild) = cache.read().guild(7) {
/// println!("Guild name: {}", guild.read().name);
/// }
/// # Ok(())
/// # }
- /// #
- /// # fn main() {
- /// # try_main().unwrap();
- /// # }
/// ```
#[inline]
pub fn guild<G: Into<GuildId>>(&self, id: G) -> Option<Arc<RwLock<Guild>>> {
@@ -438,13 +466,11 @@ impl Cache {
/// # use serenity::model::prelude::*;
/// # use serenity::prelude::*;
/// #
- /// use serenity::CACHE;
- ///
/// struct Handler;
///
/// impl EventHandler for Handler {
- /// fn message(&self, ctx: Context, message: Message) {
- /// let cache = CACHE.read();
+ /// fn message(&self, context: Context, message: Message) {
+ /// let cache = context.cache.read();
///
/// let channel = match cache.guild_channel(message.channel_id) {
/// Some(channel) => channel,
@@ -496,20 +522,20 @@ impl Cache {
/// Retrieve a group from the cache and print its owner's id:
///
/// ```rust,no_run
- /// # use std::error::Error;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
/// #
- /// # fn try_main() -> Result<(), Box<Error>> {
- /// use serenity::CACHE;
- ///
- /// if let Some(group) = CACHE.read().group(7) {
+ /// # use serenity::cache::Cache;
+ /// # use parking_lot::RwLock;
+ /// # use std::{error::Error, sync::Arc};
+ /// #
+ /// # fn main() -> Result<(), Box<Error>> {
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// if let Some(group) = cache.read().group(7) {
/// println!("Owner Id: {}", group.read().owner_id);
/// }
/// # Ok(())
/// # }
- /// #
- /// # fn main() {
- /// # try_main().unwrap();
- /// # }
/// ```
#[inline]
pub fn group<C: Into<ChannelId>>(&self, id: C) -> Option<Arc<RwLock<Group>>> {
@@ -532,9 +558,16 @@ impl Cache {
/// [`Client::on_message`] context:
///
/// ```rust,ignore
- /// use serenity::CACHE;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
+ /// #
+ /// # use serenity::{cache::Cache, model::prelude::*, prelude::*};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
+ /// #
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// let cache = cache.read();
///
- /// let cache = CACHE.read();
/// let member = {
/// let channel = match cache.guild_channel(message.channel_id) {
/// Some(channel) => channel,
@@ -589,12 +622,19 @@ impl Cache {
/// name:
///
/// ```rust,no_run
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
+ /// #
/// # use std::error::Error;
/// #
+ /// # use serenity::{cache::Cache, model::prelude::*, prelude::*};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
+ /// #
/// # fn try_main() -> Result<(), Box<Error>> {
- /// use serenity::CACHE;
- ///
- /// let cache = CACHE.read();
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// # let cache = cache.read();
+ /// // assuming the cache has been unlocked
///
/// if let Some(channel) = cache.private_channel(7) {
/// let channel_reader = channel.read();
@@ -635,20 +675,21 @@ impl Cache {
/// Retrieve a role from the cache and print its name:
///
/// ```rust,no_run
- /// # use std::error::Error;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
/// #
- /// # fn try_main() -> Result<(), Box<Error>> {
- /// use serenity::CACHE;
- ///
- /// if let Some(role) = CACHE.read().role(7, 77) {
+ /// # use serenity::cache::Cache;
+ /// # use parking_lot::RwLock;
+ /// # use std::{error::Error, sync::Arc};
+ /// #
+ /// # fn main() -> Result<(), Box<Error>> {
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// // assuming the cache is in scope, e.g. via `Context`
+ /// if let Some(role) = cache.read().role(7, 77) {
/// println!("Role with Id 77 is called {}", role.name);
/// }
/// # Ok(())
/// # }
- /// #
- /// # fn main() {
- /// # try_main().unwrap();
- /// # }
/// ```
#[inline]
pub fn role<G, R>(&self, guild_id: G, role_id: R) -> Option<Role>
@@ -707,20 +748,14 @@ impl Cache {
/// Retrieve a user from the cache and print their name:
///
/// ```rust,no_run
+ /// # use serenity::command;
/// # use std::error::Error;
/// #
- /// # fn try_main() -> Result<(), Box<Error>> {
- /// use serenity::CACHE;
- ///
- /// if let Some(user) = CACHE.read().user(7) {
+ /// # command!(test(context) {
+ /// if let Some(user) = context.cache.read().user(7) {
/// println!("User with Id 7 is currently named {}", user.read().name);
/// }
- /// # Ok(())
- /// # }
- /// #
- /// # fn main() {
- /// # try_main().unwrap();
- /// # }
+ /// # });
/// ```
#[inline]
pub fn user<U: Into<UserId>>(&self, user_id: U) -> Option<Arc<RwLock<User>>> {
@@ -757,15 +792,6 @@ impl Cache {
e.update(self)
}
- /// Gets the duration it will try for when acquiring a write lock.
- ///
- /// Refer to the documentation for [`cache_lock_time`] for more information.
- ///
- /// [`cache_lock_time`]: struct.Settings.html#method.cache_lock_time
- pub fn get_try_write_duration(&self) -> Option<Duration> {
- self.settings.cache_lock_time
- }
-
pub(crate) fn update_user_entry(&mut self, user: &User) {
match self.users.entry(user.id) {
Entry::Vacant(e) => {
diff --git a/src/cache/settings.rs b/src/cache/settings.rs
index 975ddc6..3fabb5a 100644
--- a/src/cache/settings.rs
+++ b/src/cache/settings.rs
@@ -1,5 +1,3 @@
-use std::time::Duration;
-
/// Settings for the cache.
///
/// # Examples
@@ -18,15 +16,6 @@ pub struct Settings {
///
/// Defaults to 0.
pub max_messages: usize,
-
- /// The Duration cache updates will try to acquire write-locks for.
- ///
- /// Defaults to 10 milliseconds.
- ///
- /// **Note**:
- /// If set to `None`, cache updates will acquire write-lock until available,
- /// potentially deadlocking.
- pub cache_lock_time: Option<Duration>,
__nonexhaustive: (),
}
@@ -34,7 +23,6 @@ impl Default for Settings {
fn default() -> Self {
Settings {
max_messages: usize::default(),
- cache_lock_time: Some(Duration::from_millis(10)),
__nonexhaustive: (),
}
}
@@ -68,46 +56,4 @@ impl Settings {
self
}
-
- /// Sets the duration that the cache will try to aquire a write lock.
- ///
- /// Refer to [`cache_lock_time`] for more information.
- ///
- /// **Note**:
- /// Should be set before the client gets started, as it can not be
- /// changed after the first read of the duration.
- ///
- /// # Examples
- ///
- /// Set the time that it will try to aquire a lock.
- ///
- /// ```rust,no_run
- /// use std::time::Duration;
- /// use std::env;
- /// use serenity::prelude::*;
- ///
- /// struct Handler;
- ///
- /// impl EventHandler for Handler {}
- ///
- /// fn main() {
- /// let token = env::var("DISCORD_TOKEN")
- /// .expect("Expected a token in the environment");
- /// serenity::CACHE
- /// .write().settings_mut()
- /// .cache_lock_time(Some(Duration::from_secs(1)));
- /// let mut client = Client::new(&token, Handler).unwrap();
- ///
- /// if let Err(why) = client.start() {
- /// println!("Client error: {:?}", why);
- /// }
- /// }
- /// ```
- ///
- /// [`cache_lock_time`]: #structfield.cache_lock_time
- pub fn cache_lock_time(&mut self, duration: Option<Duration>) -> &mut Self {
- self.cache_lock_time = duration;
-
- self
- }
}
diff --git a/src/client/bridge/gateway/shard_manager.rs b/src/client/bridge/gateway/shard_manager.rs
index 45926ef..579b003 100644
--- a/src/client/bridge/gateway/shard_manager.rs
+++ b/src/client/bridge/gateway/shard_manager.rs
@@ -1,5 +1,6 @@
use crate::gateway::InterMessage;
use crate::internal::prelude::*;
+use crate::CacheAndHttp;
use parking_lot::Mutex;
use std::{
collections::{HashMap, VecDeque},
@@ -50,14 +51,17 @@ use crate::client::bridge::voice::ClientVoiceManager;
/// # use serenity::client::bridge::voice::ClientVoiceManager;
/// # #[cfg(feature = "voice")]
/// # use serenity::model::id::UserId;
+/// # #[cfg(feature = "cache")]
+/// # use serenity::cache::Cache;
/// #
/// # #[cfg(feature = "framework")]
/// # fn try_main() -> Result<(), Box<Error>> {
/// #
-/// use parking_lot::Mutex;
+/// use parking_lot::{Mutex, RwLock};
/// use serenity::client::bridge::gateway::{ShardManager, ShardManagerOptions};
/// use serenity::client::EventHandler;
/// use serenity::http;
+/// use serenity::CacheAndHttp;
/// use std::sync::Arc;
/// use std::env;
/// use threadpool::ThreadPool;
@@ -76,6 +80,7 @@ use crate::client::bridge::voice::ClientVoiceManager;
/// let event_handler = Arc::new(Handler);
/// let framework = Arc::new(Mutex::new(None));
/// let threadpool = ThreadPool::with_name("my threadpool".to_owned(), 5);
+/// let cache_and_http = Arc::new(CacheAndHttp::default());
///
/// ShardManager::new(ShardManagerOptions {
/// data: &data,
@@ -92,6 +97,7 @@ use crate::client::bridge::voice::ClientVoiceManager;
/// # #[cfg(feature = "voice")]
/// # voice_manager: &Arc::new(Mutex::new(ClientVoiceManager::new(0, UserId(0)))),
/// ws_url: &gateway_url,
+/// cache_and_http: &cache_and_http,
/// });
/// # Ok(())
/// # }
@@ -151,6 +157,7 @@ impl ShardManager {
#[cfg(feature = "voice")]
voice_manager: Arc::clone(opt.voice_manager),
ws_url: Arc::clone(opt.ws_url),
+ cache_and_http: Arc::clone(&opt.cache_and_http),
};
thread::spawn(move || {
@@ -362,4 +369,5 @@ pub struct ShardManagerOptions<'a, H: EventHandler + Send + Sync + 'static> {
#[cfg(feature = "voice")]
pub voice_manager: &'a Arc<Mutex<ClientVoiceManager>>,
pub ws_url: &'a Arc<Mutex<String>>,
+ pub cache_and_http: &'a Arc<CacheAndHttp>,
}
diff --git a/src/client/bridge/gateway/shard_queuer.rs b/src/client/bridge/gateway/shard_queuer.rs
index 5152249..12df4c8 100644
--- a/src/client/bridge/gateway/shard_queuer.rs
+++ b/src/client/bridge/gateway/shard_queuer.rs
@@ -1,5 +1,6 @@
use crate::gateway::Shard;
use crate::internal::prelude::*;
+use crate::CacheAndHttp;
use parking_lot::Mutex;
use std::{
collections::{HashMap, VecDeque},
@@ -85,6 +86,7 @@ pub struct ShardQueuer<H: EventHandler + Send + Sync + 'static> {
pub voice_manager: Arc<Mutex<ClientVoiceManager>>,
/// A copy of the URI to use to connect to the gateway.
pub ws_url: Arc<Mutex<String>>,
+ pub cache_and_http: Arc<CacheAndHttp>,
}
impl<H: EventHandler + Send + Sync + 'static> ShardQueuer<H> {
@@ -188,6 +190,7 @@ impl<H: EventHandler + Send + Sync + 'static> ShardQueuer<H> {
#[cfg(feature = "voice")]
voice_manager: Arc::clone(&self.voice_manager),
shard,
+ cache_and_http: Arc::clone(&self.cache_and_http),
});
let runner_info = ShardRunnerInfo {
diff --git a/src/client/bridge/gateway/shard_runner.rs b/src/client/bridge/gateway/shard_runner.rs
index 5e2a1ff..fe71462 100644
--- a/src/client/bridge/gateway/shard_runner.rs
+++ b/src/client/bridge/gateway/shard_runner.rs
@@ -2,6 +2,7 @@ use crate::gateway::{InterMessage, ReconnectType, Shard, ShardAction};
use crate::internal::prelude::*;
use crate::internal::ws_impl::{ReceiverExt, SenderExt};
use crate::model::event::{Event, GatewayEvent};
+use crate::CacheAndHttp;
use parking_lot::Mutex;
use serde::Deserialize;
use std::{
@@ -32,6 +33,7 @@ use crate::framework::Framework;
#[cfg(feature = "voice")]
use super::super::voice::ClientVoiceManager;
+
/// A runner for managing a [`Shard`] and its respective WebSocket client.
///
/// [`Shard`]: ../../../gateway/struct.Shard.html
@@ -49,6 +51,7 @@ pub struct ShardRunner<H: EventHandler + Send + Sync + 'static> {
threadpool: ThreadPool,
#[cfg(feature = "voice")]
voice_manager: Arc<Mutex<ClientVoiceManager>>,
+ cache_and_http: Arc<CacheAndHttp>,
}
impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> {
@@ -68,6 +71,7 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> {
threadpool: opt.threadpool,
#[cfg(feature = "voice")]
voice_manager: opt.voice_manager,
+ cache_and_http: opt.cache_and_http,
}
}
@@ -212,6 +216,7 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> {
&self.runner_tx,
&self.threadpool,
self.shard.shard_info()[0],
+ Arc::clone(&self.cache_and_http),
);
}
@@ -493,4 +498,6 @@ pub struct ShardRunnerOptions<H: EventHandler + Send + Sync + 'static> {
pub threadpool: ThreadPool,
#[cfg(feature = "voice")]
pub voice_manager: Arc<Mutex<ClientVoiceManager>>,
+ #[cfg(feature = "cache")]
+ pub cache_and_http: Arc<CacheAndHttp>,
}
diff --git a/src/client/context.rs b/src/client/context.rs
index 8a08213..ff92f5e 100644
--- a/src/client/context.rs
+++ b/src/client/context.rs
@@ -1,5 +1,4 @@
use crate::builder::EditProfile;
-use crate::CACHE;
use crate::client::bridge::gateway::ShardMessenger;
use crate::error::Result;
use crate::gateway::InterMessage;
@@ -14,6 +13,10 @@ use std::sync::{
use typemap::ShareMap;
use crate::utils::VecMap;
use crate::utils::vecmap_to_json_map;
+#[cfg(feature = "cache")]
+pub use crate::cache::Cache;
+#[cfg(feature = "cache")]
+use parking_lot::RwLock;
/// The context is a general utility struct provided on event dispatches, which
/// helps with dealing with the current "context" of the event dispatch.
@@ -41,6 +44,8 @@ pub struct Context {
pub shard: ShardMessenger,
/// The ID of the shard this context is related to.
pub shard_id: u64,
+ #[cfg(feature = "cache")]
+ pub cache: Arc<RwLock<Cache>>,
}
impl Context {
@@ -49,11 +54,14 @@ impl Context {
data: Arc<Mutex<ShareMap>>,
runner_tx: Sender<InterMessage>,
shard_id: u64,
+ cache: Arc<RwLock<Cache>>,
) -> Context {
Context {
shard: ShardMessenger::new(runner_tx),
shard_id,
data,
+ #[cfg(feature = "cache")]
+ cache,
}
}
@@ -94,7 +102,7 @@ impl Context {
feature_cache! {
{
- let cache = CACHE.read();
+ let cache = self.cache.read();
map.insert("username", Value::String(cache.user.name.clone()));
diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs
index 5ed5f3e..0425f29 100644
--- a/src/client/dispatch.rs
+++ b/src/client/dispatch.rs
@@ -4,14 +4,13 @@ use crate::model::{
event::Event,
guild::Member,
};
-use std::sync::Arc;
+use std::{sync::{Arc, mpsc::Sender}};
use parking_lot::Mutex;
use super::{
bridge::gateway::event::ClientEvent,
event_handler::EventHandler,
Context
};
-use std::sync::mpsc::Sender;
use threadpool::ThreadPool;
use typemap::ShareMap;
@@ -20,42 +19,45 @@ use crate::framework::Framework;
#[cfg(feature = "cache")]
use crate::model::id::GuildId;
#[cfg(feature = "cache")]
-use std::time::Duration;
-
+use crate::cache::Cache;
#[cfg(feature = "cache")]
-use super::CACHE;
-
+use crate::CacheAndHttp;
#[cfg(feature = "cache")]
-lazy_static! {
- pub static ref CACHE_TRY_WRITE_DURATION: Option<Duration> =
- CACHE.read().get_try_write_duration();
-}
+use parking_lot::RwLock;
macro_rules! update {
- ($event:expr) => {
+ ($cache_and_http:ident, $event:expr) => {
{
#[cfg(feature = "cache")]
{
- match *CACHE_TRY_WRITE_DURATION {
- Some(duration) => {
- if let Some(mut lock) = CACHE.try_write_for(duration) {
- lock.update(&mut $event)
- } else {
- warn!(
- "[dispatch] Possible deadlock: couldn't unlock cache to update with event: {:?}",
- $event,
- );
- None
- }},
- None => {
- CACHE.write().update(&mut $event)
- },
+ if let Some(millis_timeout) = $cache_and_http.update_cache_timeout {
+
+ if let Some(mut lock) = $cache_and_http.cache.try_write_for(millis_timeout) {
+ lock.update(&mut $event)
+ } else {
+ warn!("[dispatch] Possible deadlock: Couldn't unlock cache to update with event: {:?}", $event);
+
+ None
+ }
+ } else {
+ $cache_and_http.cache.write().update(&mut $event)
}
}
}
- };
+ }
}
+#[cfg(feature = "cache")]
+fn context(
+ data: &Arc<Mutex<ShareMap>>,
+ runner_tx: &Sender<InterMessage>,
+ shard_id: u64,
+ cache: &Arc<RwLock<Cache>>,
+) -> Context {
+ Context::new(Arc::clone(data), runner_tx.clone(), shard_id, cache.clone())
+}
+
+#[cfg(not(feature = "cache"))]
fn context(
data: &Arc<Mutex<ShareMap>>,
runner_tx: &Sender<InterMessage>,
@@ -79,12 +81,17 @@ pub(crate) fn dispatch<H: EventHandler + Send + Sync + 'static>(
runner_tx: &Sender<InterMessage>,
threadpool: &ThreadPool,
shard_id: u64,
+ cache_and_http: Arc<CacheAndHttp>,
) {
match event {
DispatchEvent::Model(Event::MessageCreate(mut event)) => {
- update!(event);
+ update!(cache_and_http, event);
+ #[cfg(feature = "cache")]
+ let context = context(data, runner_tx, shard_id, &cache_and_http.cache);
+ #[cfg(not(feature = "cache"))]
let context = context(data, runner_tx, shard_id);
+
dispatch_message(
context.clone(),
event.message.clone(),
@@ -103,6 +110,7 @@ pub(crate) fn dispatch<H: EventHandler + Send + Sync + 'static>(
runner_tx,
threadpool,
shard_id,
+ cache_and_http,
),
}
}
@@ -116,12 +124,12 @@ pub(crate) fn dispatch<H: EventHandler + Send + Sync + 'static>(
runner_tx: &Sender<InterMessage>,
threadpool: &ThreadPool,
shard_id: u64,
+ cache_and_http: Arc<CacheAndHttp>,
) {
match event {
DispatchEvent::Model(Event::MessageCreate(mut event)) => {
- update!(event);
+ update!(cache_and_http, event);
- let context = context(data, runner_tx, shard_id);
dispatch_message(context, event.message, event_handler, threadpool);
},
other => handle_event(
@@ -131,6 +139,7 @@ pub(crate) fn dispatch<H: EventHandler + Send + Sync + 'static>(
runner_tx,
threadpool,
shard_id,
+ cache_and_http,
),
}
}
@@ -162,10 +171,15 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
runner_tx: &Sender<InterMessage>,
threadpool: &ThreadPool,
shard_id: u64,
+ cache_and_http: Arc<CacheAndHttp>,
) {
+ #[cfg(feature = "cache")]
+ let context = context(data, runner_tx, shard_id, &cache_and_http.cache);
+ #[cfg(not(feature = "cache"))]
+ let context = context(data, runner_tx, shard_id);
+
match event {
DispatchEvent::Client(ClientEvent::ShardStageUpdate(event)) => {
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -173,10 +187,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
}
DispatchEvent::Model(Event::ChannelCreate(mut event)) => {
- update!(event);
-
- let context = context(data, runner_tx, shard_id);
-
+ update!(cache_and_http, event);
// Discord sends both a MessageCreate and a ChannelCreate upon a new message in a private channel.
// This could potentially be annoying to handle when otherwise wanting to normally take care of a new channel.
// So therefore, private channels are dispatched to their own handler code.
@@ -206,9 +217,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
}
},
DispatchEvent::Model(Event::ChannelDelete(mut event)) => {
- update!(event);
-
- let context = context(data, runner_tx, shard_id);
+ update!(cache_and_http, event);
match event.channel {
Channel::Private(_) | Channel::Group(_) => {},
@@ -229,7 +238,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
}
},
DispatchEvent::Model(Event::ChannelPinsUpdate(mut event)) => {
- let context = context(data, runner_tx, shard_id);
+
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -237,9 +246,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::ChannelRecipientAdd(mut event)) => {
- update!(event);
-
- let context = context(data, runner_tx, shard_id);
+ update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
@@ -252,9 +259,8 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::ChannelRecipientRemove(mut event)) => {
- update!(event);
+ update!(cache_and_http, event);
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -266,14 +272,13 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::ChannelUpdate(mut event)) => {
- update!(event);
+ update!(cache_and_http, event);
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
feature_cache! {{
- let before = CACHE.read().channel(event.channel.id());
+ let before = cache_and_http.cache.read().channel(event.channel.id());
event_handler.channel_update(context, before, event.channel);
} else {
@@ -282,7 +287,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildBanAdd(mut event)) => {
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -290,7 +294,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildBanRemove(mut event)) => {
- let context = context(data, runner_tx, shard_id);
+
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -300,21 +304,20 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
DispatchEvent::Model(Event::GuildCreate(mut event)) => {
#[cfg(feature = "cache")]
let _is_new = {
- let cache = CACHE.read();
+ let cache = cache_and_http.cache.read();
!cache.unavailable_guilds.contains(&event.guild.id)
};
- update!(event);
+ update!(cache_and_http, event);
#[cfg(feature = "cache")]
{
- let cache = CACHE.read();
-
- if cache.unavailable_guilds.is_empty() {
- let context = context(data, runner_tx, shard_id);
+ let locked_cache = cache_and_http.cache.read();
+ let context = context.clone();
- let guild_amount = cache
+ if locked_cache.unavailable_guilds.is_empty() {
+ let guild_amount = locked_cache
.guilds
.iter()
.map(|(&id, _)| id)
@@ -327,7 +330,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
}
}
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -339,8 +341,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildDelete(mut event)) => {
- let _full = update!(event);
- let context = context(data, runner_tx, shard_id);
+ let _full = update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -352,9 +353,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildEmojisUpdate(mut event)) => {
- update!(event);
-
- let context = context(data, runner_tx, shard_id);
+ update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -362,7 +361,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildIntegrationsUpdate(mut event)) => {
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -370,9 +368,8 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildMemberAdd(mut event)) => {
- update!(event);
+ update!(cache_and_http, event);
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -380,8 +377,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildMemberRemove(mut event)) => {
- let _member = update!(event);
- let context = context(data, runner_tx, shard_id);
+ let _member = update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -393,15 +389,13 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildMemberUpdate(mut event)) => {
- let _before = update!(event);
-
+ let _before = update!(cache_and_http, event);
let _after: Option<Member> = feature_cache! {{
- CACHE.read().member(event.guild_id, event.user.id)
+ cache_and_http.cache.read().member(event.guild_id, event.user.id)
} else {
None
}};
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -415,9 +409,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildMembersChunk(mut event)) => {
- update!(event);
-
- let context = context(data, runner_tx, shard_id);
+ update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -425,9 +417,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildRoleCreate(mut event)) => {
- update!(event);
-
- let context = context(data, runner_tx, shard_id);
+ update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -435,8 +425,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildRoleDelete(mut event)) => {
- let _role = update!(event);
- let context = context(data, runner_tx, shard_id);
+ let _role = update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -448,8 +437,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildRoleUpdate(mut event)) => {
- let _before = update!(event);
- let context = context(data, runner_tx, shard_id);
+ let _before = update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -461,9 +449,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildUnavailable(mut event)) => {
- update!(event);
-
- let context = context(data, runner_tx, shard_id);
+ update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -471,14 +457,12 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::GuildUpdate(mut event)) => {
- update!(event);
-
- let context = context(data, runner_tx, shard_id);
+ update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
feature_cache! {{
- let before = CACHE.read()
+ let before = cache_and_http.cache.read()
.guilds
.get(&event.guild.id)
.cloned();
@@ -492,7 +476,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
// Already handled by the framework check macro
DispatchEvent::Model(Event::MessageCreate(_)) => {},
DispatchEvent::Model(Event::MessageDeleteBulk(mut event)) => {
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -500,7 +483,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::MessageDelete(mut event)) => {
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -508,9 +490,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::MessageUpdate(mut event)) => {
- update!(event);
-
- let context = context(data, runner_tx, shard_id);
+ update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -518,9 +498,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::PresencesReplace(mut event)) => {
- update!(event);
-
- let context = context(data, runner_tx, shard_id);
+ update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -528,9 +506,8 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::PresenceUpdate(mut event)) => {
- update!(event);
+ update!(cache_and_http, event);
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -538,7 +515,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::ReactionAdd(mut event)) => {
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -546,7 +522,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::ReactionRemove(mut event)) => {
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -554,7 +529,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::ReactionRemoveAll(mut event)) => {
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -562,9 +536,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::Ready(mut event)) => {
- update!(event);
-
- let context = context(data, runner_tx, shard_id);
+ update!(cache_and_http, event);
let event_handler = Arc::clone(&event_handler);
threadpool.execute(move || {
@@ -572,12 +544,9 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::Resumed(mut event)) => {
- let context = context(data, runner_tx, shard_id);
-
event_handler.resume(context, event);
},
DispatchEvent::Model(Event::TypingStart(mut event)) => {
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -585,7 +554,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::Unknown(mut event)) => {
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -593,8 +561,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::UserUpdate(mut event)) => {
- let _before = update!(event);
- let context = context(data, runner_tx, shard_id);
+ let _before = update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -606,7 +573,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::VoiceServerUpdate(mut event)) => {
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -614,9 +580,7 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::VoiceStateUpdate(mut event)) => {
- update!(event);
-
- let context = context(data, runner_tx, shard_id);
+ update!(cache_and_http, event);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
@@ -624,7 +588,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
});
},
DispatchEvent::Model(Event::WebhookUpdate(mut event)) => {
- let context = context(data, runner_tx, shard_id);
let event_handler = Arc::clone(event_handler);
threadpool.execute(move || {
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 4bd9d11..bbedf89 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -32,18 +32,22 @@ pub use self::{
event_handler::EventHandler
};
+pub use crate::CacheAndHttp;
+
// Note: the following re-exports are here for backwards compatibility
pub use crate::gateway;
pub use crate::http as rest;
#[cfg(feature = "cache")]
-pub use crate::CACHE;
+pub use crate::cache::Cache;
+#[cfg(feature = "cache")]
+use parking_lot::RwLock;
use crate::http;
use crate::internal::prelude::*;
use parking_lot::Mutex;
use self::bridge::gateway::{ShardManager, ShardManagerMonitor, ShardManagerOptions};
-use std::sync::Arc;
+use std::{sync::Arc, time::Duration};
use threadpool::ThreadPool;
use typemap::ShareMap;
@@ -295,6 +299,7 @@ pub struct Client {
/// This is wrapped in an `Arc<Mutex<T>>` so all shards will have an updated
/// value available.
pub ws_uri: Arc<Mutex<String>>,
+ pub cache_and_http: Arc<CacheAndHttp>,
}
impl Client {
@@ -354,6 +359,115 @@ impl Client {
UserId(0),
)));
+ let cache_and_http = Arc::new(CacheAndHttp {
+ #[cfg(feature = "cache")]
+ cache: Arc::new(RwLock::new(Cache::default())),
+ #[cfg(feature = "cache")]
+ update_cache_timeout: None,
+ __nonexhaustive: (),
+ });
+
+ let (shard_manager, shard_manager_worker) = {
+ ShardManager::new(ShardManagerOptions {
+ data: &data,
+ event_handler: &event_handler,
+ #[cfg(feature = "framework")]
+ framework: &framework,
+ shard_index: 0,
+ shard_init: 0,
+ shard_total: 0,
+ threadpool: threadpool.clone(),
+ token: &locked,
+ #[cfg(feature = "voice")]
+ voice_manager: &voice_manager,
+ ws_url: &url,
+ cache_and_http: &cache_and_http,
+ })
+ };
+
+ Ok(Client {
+ token: locked,
+ ws_uri: url,
+ #[cfg(feature = "framework")]
+ framework,
+ data,
+ shard_manager,
+ shard_manager_worker,
+ threadpool,
+ #[cfg(feature = "voice")]
+ voice_manager,
+ cache_and_http,
+ })
+ }
+
+ /// Creates a Client for a bot user and sets a cache update timeout.
+ /// If set to some duration, updating the cache will try to claim a
+ /// write-lock for given duration and skip received event but also
+ /// issue a deadlock-warning upon failure.
+ /// If `duration` is set to `None`, updating the cache will try to claim
+ /// a write-lock until success and potentially deadlock.
+ ///
+ /// Discord has a requirement of prefixing bot tokens with `"Bot "`, which
+ /// this function will automatically do for you if not already included.
+ ///
+ /// # Examples
+ ///
+ /// Create a Client, using a token from an environment variable:
+ ///
+ /// ```rust,no_run
+ /// # use serenity::prelude::EventHandler;
+ /// struct Handler;
+ ///
+ /// impl EventHandler for Handler {}
+ /// # use std::error::Error;
+ /// #
+ /// # fn try_main() -> Result<(), Box<Error>> {
+ /// use serenity::Client;
+ /// use std::env;
+ ///
+ /// let token = env::var("DISCORD_TOKEN")?;
+ /// let client = Client::new_with_cache_update_timeout(&token, Handler, None)?;
+ /// # Ok(())
+ /// # }
+ /// #
+ /// # fn main() {
+ /// # try_main().unwrap();
+ /// # }
+ /// ```
+ #[cfg(feature = "cache")]
+ pub fn new_with_cache_update_timeout<H>(token: &str, handler: H, duration: Option<Duration>) -> Result<Self>
+ where H: EventHandler + Send + Sync + 'static {
+ let token = token.trim();
+
+ let token = if token.starts_with("Bot ") {
+ token.to_string()
+ } else {
+ format!("Bot {}", token)
+ };
+
+ http::set_token(&token);
+ let locked = Arc::new(Mutex::new(token));
+
+ let name = "serenity client".to_owned();
+ let threadpool = ThreadPool::with_name(name, 5);
+ let url = Arc::new(Mutex::new(http::get_gateway()?.url));
+ let data = Arc::new(Mutex::new(ShareMap::custom()));
+ let event_handler = Arc::new(handler);
+
+ #[cfg(feature = "framework")]
+ let framework = Arc::new(Mutex::new(None));
+ #[cfg(feature = "voice")]
+ let voice_manager = Arc::new(Mutex::new(ClientVoiceManager::new(
+ 0,
+ UserId(0),
+ )));
+
+ let cache_and_http = Arc::new(CacheAndHttp {
+ cache: Arc::new(RwLock::new(Cache::default())),
+ update_cache_timeout: duration,
+ __nonexhaustive: (),
+ });
+
let (shard_manager, shard_manager_worker) = {
ShardManager::new(ShardManagerOptions {
data: &data,
@@ -368,6 +482,7 @@ impl Client {
#[cfg(feature = "voice")]
voice_manager: &voice_manager,
ws_url: &url,
+ cache_and_http: &cache_and_http,
})
};
@@ -382,6 +497,7 @@ impl Client {
threadpool,
#[cfg(feature = "voice")]
voice_manager,
+ cache_and_http,
})
}
diff --git a/src/framework/standard/command.rs b/src/framework/standard/command.rs
index 964f788..d4cbcae 100644
--- a/src/framework/standard/command.rs
+++ b/src/framework/standard/command.rs
@@ -373,7 +373,7 @@ pub fn positions(ctx: &mut Context, msg: &Message, conf: &Configuration) -> Opti
#[cfg(feature = "cache")]
{
- let private = match msg.channel() {
+ let private = match msg.channel(&ctx.cache) {
Some(Channel::Private(_)) => true,
_ => false,
};
diff --git a/src/framework/standard/help_commands.rs b/src/framework/standard/help_commands.rs
index bde965b..a5aa9b1 100644
--- a/src/framework/standard/help_commands.rs
+++ b/src/framework/standard/help_commands.rs
@@ -26,6 +26,10 @@
use crate::client::Context;
#[cfg(feature = "cache")]
use crate::framework::standard::{has_correct_roles, has_correct_permissions};
+#[cfg(feature = "cache")]
+use crate::cache::Cache;
+#[cfg(feature = "cache")]
+use parking_lot::RwLock;
use crate::model::{
channel::Message,
id::ChannelId,
@@ -237,18 +241,18 @@ fn remove_aliases(cmds: &HashMap<String, CommandOrAlias>) -> HashMap<&String, &I
/// Checks whether a user is member of required roles
/// and given the required permissions.
#[cfg(feature = "cache")]
-pub fn has_all_requirements(cmd: &Arc<CommandOptions>, msg: &Message) -> bool {
- if let Some(guild) = msg.guild() {
+pub fn has_all_requirements(cache: &Arc<RwLock<Cache>>, cmd: &Arc<CommandOptions>, msg: &Message) -> bool {
+ if let Some(guild) = msg.guild(&cache) {
let guild = guild.read();
if let Some(member) = guild.members.get(&msg.author.id) {
- if let Ok(permissions) = member.permissions() {
+ if let Ok(permissions) = member.permissions(&cache) {
return if cmd.allowed_roles.is_empty() {
- permissions.administrator() || has_correct_permissions(cmd, msg)
+ permissions.administrator() || has_correct_permissions(&cache, cmd, msg)
} else {
- permissions.administrator() || (has_correct_roles(cmd, &guild, member) && has_correct_permissions(cmd, msg))
+ permissions.administrator() || (has_correct_roles(cmd, &guild, member) && has_correct_permissions(&cache, cmd, msg))
}
}
}
@@ -262,20 +266,22 @@ pub fn has_all_requirements(cmd: &Arc<CommandOptions>, msg: &Message) -> bool {
/// **Note**: A command is visible when it is either normally displayed or
/// strikethrough upon requested help by a user.
#[cfg(feature = "cache")]
-pub fn is_command_visible(command_options: &Arc<CommandOptions>, msg: &Message, help_options: &HelpOptions) -> bool {
+pub fn is_command_visible(cache: &Arc<RwLock<Cache>>, command_options: &Arc<CommandOptions>, msg: &Message,
+ help_options: &HelpOptions) -> bool {
+
if !command_options.dm_only && !command_options.guild_only
|| command_options.dm_only && msg.is_private()
|| command_options.guild_only && !msg.is_private()
{
- if let Some(guild) = msg.guild() {
+ if let Some(guild) = msg.guild(&cache) {
let guild = guild.read();
if let Some(member) = guild.members.get(&msg.author.id) {
if command_options.help_available {
- return if has_correct_permissions(command_options, msg) {
+ return if has_correct_permissions(&cache, command_options, msg) {
if has_correct_roles(command_options, &guild, &member) {
true
@@ -288,7 +294,7 @@ pub fn is_command_visible(command_options: &Arc<CommandOptions>, msg: &Message,
}
}
} else if command_options.help_available {
- return if has_correct_permissions(command_options, msg) {
+ return if has_correct_permissions(&cache, command_options, msg) {
true
} else {
help_options.lacking_permissions != HelpBehaviour::Hide
@@ -305,6 +311,7 @@ pub fn is_command_visible(command_options: &Arc<CommandOptions>, msg: &Message,
/// returns similar commands.
#[cfg(feature = "cache")]
fn fetch_single_command<'a, H: BuildHasher>(
+ cache: &Arc<RwLock<Cache>>,
groups: &'a HashMap<String, Arc<CommandGroup>, H>,
name: &str,
help_options: &'a HelpOptions,
@@ -329,7 +336,7 @@ fn fetch_single_command<'a, H: BuildHasher>(
match *command {
CommandOrAlias::Command(ref cmd) => {
- if is_command_visible(&cmd.options(), msg, help_options) {
+ if is_command_visible(&cache, &cmd.options(), msg, help_options) {
found = Some((command_name, cmd));
} else {
break;
@@ -340,7 +347,7 @@ fn fetch_single_command<'a, H: BuildHasher>(
match *actual_command {
CommandOrAlias::Command(ref cmd) => {
- if is_command_visible(&cmd.options(), msg, help_options) {
+ if is_command_visible(&cache, &cmd.options(), msg, help_options) {
found = Some((name, cmd));
} else {
break;
@@ -374,7 +381,7 @@ fn fetch_single_command<'a, H: BuildHasher>(
let levenshtein_distance = levenshtein_distance(&command_name, &name);
if levenshtein_distance <= help_options.max_levenshtein_distance
- && is_command_visible(&cmd.options(), &msg, &help_options) {
+ && is_command_visible(&cache, &cmd.options(), &msg, &help_options) {
similar_commands.push(SuggestedCommandName {
name: command_name,
@@ -426,6 +433,7 @@ fn fetch_single_command<'a, H: BuildHasher>(
/// Tries to extract a single command matching searched command name.
#[cfg(feature = "cache")]
fn fetch_all_eligible_commands_in_group<'a>(
+ cache: &Arc<RwLock<Cache>>,
commands: &HashMap<&String, &InternalCommand>,
command_names: &[&&String],
help_options: &'a HelpOptions,
@@ -442,9 +450,9 @@ fn fetch_all_eligible_commands_in_group<'a>(
|| cmd.dm_only && msg.is_private()
|| cmd.guild_only && !msg.is_private()
{
- if cmd.help_available && has_correct_permissions(&cmd, msg) {
+ if cmd.help_available && has_correct_permissions(&cache, &cmd, msg) {
- if let Some(guild) = msg.guild() {
+ if let Some(guild) = msg.guild(&cache) {
let guild = guild.read();
if let Some(member) = guild.members.get(&msg.author.id) {
@@ -475,6 +483,7 @@ fn fetch_all_eligible_commands_in_group<'a>(
/// Fetch groups with their commands.
#[cfg(feature = "cache")]
fn create_command_group_commands_pair_from_groups<'a, H: BuildHasher>(
+ cache: &Arc<RwLock<Cache>>,
groups: &'a HashMap<String, Arc<CommandGroup>, H>,
group_names: &[&'a String],
msg: &Message,
@@ -486,6 +495,7 @@ fn create_command_group_commands_pair_from_groups<'a, H: BuildHasher>(
let group = &groups[&**group_name];
let group_with_cmds = create_single_group(
+ &cache,
group,
group_name,
&msg,
@@ -503,6 +513,7 @@ fn create_command_group_commands_pair_from_groups<'a, H: BuildHasher>(
/// Fetches a single group with its commands.
#[cfg(feature = "cache")]
fn create_single_group<'a>(
+ cache: &Arc<RwLock<Cache>>,
group: &CommandGroup,
group_name: &'a str,
msg: &Message,
@@ -513,6 +524,7 @@ let commands = remove_aliases(&group.commands);
command_names.sort();
let mut group_with_cmds = fetch_all_eligible_commands_in_group(
+ &cache,
&commands,
&command_names,
&help_options,
@@ -533,6 +545,7 @@ let commands = remove_aliases(&group.commands);
/// shall be picked and in what textual format.
#[cfg(feature = "cache")]
pub fn create_customised_help_data<'a, H: BuildHasher>(
+ cache: &Arc<RwLock<Cache>>,
groups: &'a HashMap<String, Arc<CommandGroup>, H>,
args: &'a Args,
help_options: &'a HelpOptions,
@@ -541,7 +554,7 @@ pub fn create_customised_help_data<'a, H: BuildHasher>(
if !args.is_empty() {
let name = args.full();
- return match fetch_single_command(&groups, &name, &help_options, &msg) {
+ return match fetch_single_command(&cache, &groups, &name, &help_options, &msg) {
Ok(single_command) => single_command,
Err(suggestions) => {
let searched_named_lowercase = name.to_lowercase();
@@ -554,6 +567,7 @@ pub fn create_customised_help_data<'a, H: BuildHasher>(
*prefix == searched_named_lowercase)) {
let mut single_group = create_single_group(
+ &cache,
&group,
&key,
&msg,
@@ -602,7 +616,7 @@ pub fn create_customised_help_data<'a, H: BuildHasher>(
group_names.sort();
let listed_groups =
- create_command_group_commands_pair_from_groups(&groups, &group_names, &msg, &help_options);
+ create_command_group_commands_pair_from_groups(&cache, &groups, &group_names, &msg, &help_options);
return if listed_groups.is_empty() {
CustomisedHelpData::NoCommandFound {
@@ -745,13 +759,13 @@ fn send_error_embed(channel_id: ChannelId, input: &str, colour: Colour) -> Resul
/// ```
#[cfg(feature = "cache")]
pub fn with_embeds<H: BuildHasher>(
- _: &mut Context,
+ context: &mut Context,
msg: &Message,
help_options: &HelpOptions,
groups: HashMap<String, Arc<CommandGroup>, H>,
args: &Args
) -> Result<(), CommandError> {
- let formatted_help = create_customised_help_data(&groups, args, help_options, msg);
+ let formatted_help = create_customised_help_data(&context.cache, &groups, args, help_options, msg);
if let Err(why) = match formatted_help {
CustomisedHelpData::SuggestedCommands { ref help_description, ref suggestions } =>
@@ -858,13 +872,13 @@ fn single_command_to_plain_string(help_options: &HelpOptions, command: &Command)
/// ```
#[cfg(feature = "cache")]
pub fn plain<H: BuildHasher>(
- _: &mut Context,
+ context: &mut Context,
msg: &Message,
help_options: &HelpOptions,
groups: HashMap<String, Arc<CommandGroup>, H>,
args: &Args
) -> Result<(), CommandError> {
- let formatted_help = create_customised_help_data(&groups, args, help_options, msg);
+ let formatted_help = create_customised_help_data(&context.cache, &groups, args, help_options, msg);
let result = match formatted_help {
CustomisedHelpData::SuggestedCommands { ref help_description, ref suggestions } =>
diff --git a/src/framework/standard/mod.rs b/src/framework/standard/mod.rs
index 7231612..c9cf600 100644
--- a/src/framework/standard/mod.rs
+++ b/src/framework/standard/mod.rs
@@ -49,9 +49,11 @@ use super::Framework;
use threadpool::ThreadPool;
#[cfg(feature = "cache")]
-use crate::client::CACHE;
+use crate::cache::Cache;
#[cfg(feature = "cache")]
use crate::model::channel::Channel;
+#[cfg(feature = "cache")]
+use parking_lot::RwLock;
/// A convenience macro for generating a struct fulfilling the [`Command`][command trait] trait.
///
@@ -472,14 +474,14 @@ impl StandardFramework {
}
#[cfg(feature = "cache")]
- fn is_blocked_guild(&self, message: &Message) -> bool {
- if let Some(Channel::Guild(channel)) = CACHE.read().channel(message.channel_id) {
+ fn is_blocked_guild(&self, cache: &Arc<RwLock<Cache>>, message: &Message) -> bool {
+ if let Some(Channel::Guild(channel)) = cache.read().channel(message.channel_id) {
let guild_id = channel.with(|g| g.guild_id);
if self.configuration.blocked_guilds.contains(&guild_id) {
return true;
}
- if let Some(guild) = guild_id.to_guild_cached() {
+ if let Some(guild) = guild_id.to_guild_cached(&cache) {
return self.configuration
.blocked_users
.contains(&guild.with(|g| g.owner_id));
@@ -560,7 +562,7 @@ impl StandardFramework {
#[cfg(feature = "cache")]
{
- if self.is_blocked_guild(message) {
+ if self.is_blocked_guild(&context.cache, message) {
return Some(DispatchError::BlockedGuild);
}
@@ -568,7 +570,7 @@ impl StandardFramework {
return Some(DispatchError::BlockedChannel);
}
- if !has_correct_permissions(command, message) {
+ if !has_correct_permissions(&context.cache, command, message) {
return Some(DispatchError::LackOfPermissions(
command.required_permissions,
));
@@ -598,11 +600,11 @@ impl StandardFramework {
#[cfg(feature = "cache")] {
if !command.allowed_roles.is_empty() {
- if let Some(guild) = message.guild() {
+ if let Some(guild) = message.guild(&context.cache) {
let guild = guild.read();
if let Some(member) = guild.members.get(&message.author.id) {
- if let Ok(permissions) = member.permissions() {
+ if let Ok(permissions) = member.permissions(&context.cache) {
if !permissions.administrator()
&& !has_correct_roles(command, &guild, member) {
@@ -880,8 +882,8 @@ impl StandardFramework {
/// use serenity::framework::StandardFramework;
///
/// client.with_framework(StandardFramework::new()
- /// .before(|_, msg, cmd_name| {
- /// if let Ok(channel) = msg.channel_id.to_channel() {
+ /// .before(|ctx, msg, cmd_name| {
+ /// if let Ok(channel) = msg.channel_id.to_channel(&ctx) {
/// // Don't run unless in nsfw channel
/// if !channel.is_nsfw() {
/// return false;
@@ -1318,11 +1320,11 @@ impl Framework for StandardFramework {
}
#[cfg(feature = "cache")]
-pub fn has_correct_permissions(command: &Arc<CommandOptions>, message: &Message) -> bool {
+pub fn has_correct_permissions(cache: &Arc<RwLock<Cache>>, command: &Arc<CommandOptions>, message: &Message) -> bool {
if command.required_permissions.is_empty() {
true
} else {
- if let Some(guild) = message.guild() {
+ if let Some(guild) = message.guild(&cache) {
let perms = guild
.with(|g| g.permissions_in(message.channel_id, message.author.id));
diff --git a/src/lib.rs b/src/lib.rs
index d732834..e824d19 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -58,8 +58,8 @@
//! }
//! }
//!
-//! command!(ping(_context, message) {
-//! let _ = message.reply("Pong!");
+//! command!(ping(context, message) {
+//! let _ = message.reply(&context, "Pong!");
//! });
//! #
//! # }
@@ -192,44 +192,14 @@ pub use crate::client::Client;
use crate::cache::Cache;
#[cfg(feature = "cache")]
use parking_lot::RwLock;
-
#[cfg(feature = "cache")]
-lazy_static! {
- /// A mutable and lazily-initialized static binding. It can be accessed
- /// across any function and in any context.
- ///
- /// This [`Cache`] instance is updated for every event received, so you do
- /// not need to maintain your own cache.
- ///
- /// See the [cache module documentation] for more details.
- ///
- /// The Cache itself is wrapped within an `RwLock`, which allows for
- /// multiple readers or at most one writer at a time across threads. This
- /// means that you may have multiple commands reading from the Cache
- /// concurrently.
- ///
- /// # Examples
- ///
- /// Retrieve the [current user][`CurrentUser`]'s Id, by opening a Read
- /// guard:
- ///
- /// ```rust,ignore
- /// use serenity::CACHE;
- ///
- /// println!("{}", CACHE.read().user.id);
- /// ```
- ///
- /// Update the cache's settings to enable caching of channels' messages:
- ///
- /// ```rust
- /// use serenity::CACHE;
- ///
- /// // Cache up to the 10 most recent messages per channel.
- /// CACHE.write().settings_mut().max_messages(10);
- /// ```
- ///
- /// [`CurrentUser`]: model/user/struct.CurrentUser.html
- /// [`Cache`]: cache/struct.Cache.html
- /// [cache module documentation]: cache/index.html
- pub static ref CACHE: RwLock<Cache> = RwLock::new(Cache::default());
+use std::{time::Duration, sync::Arc};
+
+#[derive(Default)]
+pub struct CacheAndHttp {
+ #[cfg(feature = "cache")]
+ pub cache: Arc<RwLock<Cache>>,
+ #[cfg(feature = "cache")]
+ pub update_cache_timeout: Option<Duration>,
+ __nonexhaustive: (),
}
diff --git a/src/model/channel/channel_category.rs b/src/model/channel/channel_category.rs
index f38b0f6..50720f9 100644
--- a/src/model/channel/channel_category.rs
+++ b/src/model/channel/channel_category.rs
@@ -1,5 +1,7 @@
-use crate::model::prelude::*;
+use crate::{model::prelude::*};
+#[cfg(feature = "client")]
+use crate::client::Context;
#[cfg(all(feature = "builder", feature = "model"))]
use crate::builder::EditChannel;
#[cfg(all(feature = "builder", feature = "model"))]
@@ -55,14 +57,18 @@ impl ChannelCategory {
self.id.delete_permission(permission_type)
}
- /// Deletes this category.
+
+ /// Deletes this category if required permissions are met.
+ ///
+ /// **Note**: If the `cache`-feature is enabled permissions will be checked and upon
+ /// owning the required permissions the HTTP-request will be issued.
#[inline]
- pub fn delete(&self) -> Result<()> {
+ pub fn delete(&self, context: &Context) -> Result<()> {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_CHANNELS;
- if !utils::user_has_perms(self.id, req)? {
+ if !utils::user_has_perms(&context.cache, self.id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -82,13 +88,13 @@ impl ChannelCategory {
/// category.edit(|c| c.name("test").bitrate(86400));
/// ```
#[cfg(all(feature = "builder", feature = "model", feature = "utils"))]
- pub fn edit<F>(&mut self, f: F) -> Result<()>
+ pub fn edit<F>(&mut self, context: &Context, f: F) -> Result<()>
where F: FnOnce(EditChannel) -> EditChannel {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_CHANNELS;
- if !utils::user_has_perms(self.id, req)? {
+ if !utils::user_has_perms(&context.cache, self.id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
diff --git a/src/model/channel/channel_id.rs b/src/model/channel/channel_id.rs
index 782ea5b..3f452fa 100644
--- a/src/model/channel/channel_id.rs
+++ b/src/model/channel/channel_id.rs
@@ -1,6 +1,7 @@
-use crate::internal::RwLockExt;
-use crate::model::prelude::*;
+use crate::{internal::RwLockExt, model::prelude::*};
+#[cfg(feature = "client")]
+use crate::client::Context;
#[cfg(feature = "model")]
use std::borrow::Cow;
#[cfg(feature = "model")]
@@ -13,9 +14,9 @@ use crate::builder::{
GetMessages
};
#[cfg(all(feature = "cache", feature = "model"))]
-use crate::CACHE;
+use crate::cache::Cache;
#[cfg(all(feature = "cache", feature = "model"))]
-use crate::Cache;
+use parking_lot::RwLock;
#[cfg(feature = "model")]
use crate::http::{self, AttachmentType};
#[cfg(feature = "model")]
@@ -293,8 +294,8 @@ impl ChannelId {
/// [`Channel`]: ../channel/enum.Channel.html
#[cfg(feature = "cache")]
#[inline]
- pub fn to_channel_cached(self) -> Option<Channel> {
- self._to_channel_cached(&CACHE)
+ pub fn to_channel_cached(self, cache: &Arc<RwLock<Cache>>) -> Option<Channel> {
+ self._to_channel_cached(&cache)
}
/// To allow testing pass their own cache instead of using the globale one.
@@ -307,15 +308,15 @@ impl ChannelId {
/// First attempts to find a [`Channel`] by its Id in the cache,
/// upon failure requests it via the REST API.
///
- /// **Note**: If the cache is not enabled,
- /// REST API will be used only.
+ /// **Note**: If the `cache`-feature is enabled permissions will be checked and upon
+ /// owning the required permissions the HTTP-request will be issued.
///
/// [`Channel`]: ../channel/enum.Channel.html
#[inline]
- pub fn to_channel(self) -> Result<Channel> {
+ pub fn to_channel(self, context: &Context) -> Result<Channel> {
#[cfg(feature = "cache")]
{
- if let Some(channel) = CACHE.read().channel(self) {
+ if let Some(channel) = context.cache.read().channel(self) {
return Ok(channel);
}
}
@@ -385,11 +386,11 @@ impl ChannelId {
/// Returns the name of whatever channel this id holds.
#[cfg(feature = "model")]
- pub fn name(&self) -> Option<String> {
+ pub fn name(&self, context: &Context) -> Option<String> {
use self::Channel::*;
let finding = feature_cache! {{
- Some(self.to_channel_cached())
+ Some(self.to_channel_cached(&context.cache))
} else {
None
}};
diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs
index b976b0b..0c476e6 100644
--- a/src/model/channel/guild_channel.rs
+++ b/src/model/channel/guild_channel.rs
@@ -1,8 +1,14 @@
use chrono::{DateTime, FixedOffset};
-use crate::model::prelude::*;
+use crate::{model::prelude::*};
+#[cfg(feature = "client")]
+use crate::client::Context;
#[cfg(all(feature = "cache", feature = "model"))]
-use crate::CACHE;
+use crate::cache::Cache;
+#[cfg(feature = "cache")]
+use parking_lot::RwLock;
+#[cfg(feature = "cache")]
+use std::sync::Arc;
#[cfg(feature = "model")]
use crate::builder::{
CreateInvite,
@@ -119,16 +125,17 @@ impl GuildChannel {
/// let invite = channel.create_invite(|i| i.max_uses(5));
/// ```
#[cfg(feature = "utils")]
- pub fn create_invite<F>(&self, f: F) -> Result<RichInvite>
+ pub fn create_invite<F>(&self, context: &Context, f: F) -> Result<RichInvite>
where F: FnOnce(&mut CreateInvite) -> &mut CreateInvite {
#[cfg(feature = "cache")]
{
let req = Permissions::CREATE_INVITE;
- if !utils::user_has_perms(self.id, req)? {
+ if !utils::user_has_perms(&context.cache, self.id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
+
let mut invite = CreateInvite::default();
f(&mut invite);
@@ -153,10 +160,15 @@ impl GuildChannel {
/// permissions:
///
/// ```rust,no_run
- /// # use serenity::model::id::{ChannelId, UserId};
- /// # use std::error::Error;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
/// #
- /// # fn try_main() -> Result<(), Box<Error>> {
+ /// # use serenity::{cache::Cache, model::id::{ChannelId, UserId}};
+ /// # use parking_lot::RwLock;
+ /// # use std::{error::Error, sync::Arc};
+ /// #
+ /// # fn main() -> Result<(), Box<Error>> {
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
/// # let (channel_id, user_id) = (ChannelId(0), UserId(0));
/// #
/// use serenity::model::channel::{
@@ -164,8 +176,6 @@ impl GuildChannel {
/// PermissionOverwriteType,
/// };
/// use serenity::model::{ModelError, Permissions};
- /// use serenity::CACHE;
- ///
/// let allow = Permissions::SEND_MESSAGES;
/// let deny = Permissions::SEND_TTS_MESSAGES | Permissions::ATTACH_FILES;
/// let overwrite = PermissionOverwrite {
@@ -173,18 +183,14 @@ impl GuildChannel {
/// deny: deny,
/// kind: PermissionOverwriteType::Member(user_id),
/// };
- ///
- /// let cache = CACHE.read();
+ /// # let cache = cache.read();
+ /// // assuming the cache has been unlocked
/// let channel = cache
/// .guild_channel(channel_id)
/// .ok_or(ModelError::ItemMissing)?;
///
/// channel.read().create_permission(&overwrite)?;
- /// # Ok(())
- /// # }
- /// #
- /// # fn main() {
- /// # try_main().unwrap();
+ /// # Ok(())
/// # }
/// ```
///
@@ -194,18 +200,22 @@ impl GuildChannel {
/// permissions:
///
/// ```rust,no_run
- /// # use serenity::model::id::{ChannelId, UserId};
- /// # use std::error::Error;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
+ ///
+ /// # use serenity::{cache::Cache, model::id::{ChannelId, UserId}};
+ /// # use parking_lot::RwLock;
+ /// # use std::{error::Error, sync::Arc};
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
- /// # let (channel_id, user_id) = (ChannelId(0), UserId(0));
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// # let (channel_id, user_id) = (ChannelId(0), UserId(0));
/// #
/// use serenity::model::channel::{
/// PermissionOverwrite,
/// PermissionOverwriteType,
/// };
/// use serenity::model::{ModelError, Permissions};
- /// use serenity::CACHE;
///
/// let allow = Permissions::SEND_MESSAGES;
/// let deny = Permissions::SEND_TTS_MESSAGES | Permissions::ATTACH_FILES;
@@ -215,7 +225,7 @@ impl GuildChannel {
/// kind: PermissionOverwriteType::Member(user_id),
/// };
///
- /// let cache = CACHE.read();
+ /// let cache = cache.read();
/// let channel = cache
/// .guild_channel(channel_id)
/// .ok_or(ModelError::ItemMissing)?;
@@ -247,12 +257,15 @@ impl GuildChannel {
}
/// Deletes this channel, returning the channel on a successful deletion.
- pub fn delete(&self) -> Result<Channel> {
+ ///
+ /// **Note**: If the `cache`-feature is enabled permissions will be checked and upon
+ /// owning the required permissions the HTTP-request will be issued.
+ pub fn delete(&self, context: &Context) -> Result<Channel> {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_CHANNELS;
- if !utils::user_has_perms(self.id, req)? {
+ if !utils::user_has_perms(&context.cache, self.id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -322,13 +335,13 @@ impl GuildChannel {
/// channel.edit(|c| c.name("test").bitrate(86400));
/// ```
#[cfg(feature = "utils")]
- pub fn edit<F>(&mut self, f: F) -> Result<()>
+ pub fn edit<F>(&mut self, context: &Context, f: F) -> Result<()>
where F: FnOnce(EditChannel) -> EditChannel {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_CHANNELS;
- if !utils::user_has_perms(self.id, req)? {
+ if !utils::user_has_perms(&context.cache, self.id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -380,7 +393,7 @@ impl GuildChannel {
/// **Note**: Right now this performs a clone of the guild. This will be
/// optimized in the future.
#[cfg(feature = "cache")]
- pub fn guild(&self) -> Option<Arc<RwLock<Guild>>> { CACHE.read().guild(self.guild_id) }
+ pub fn guild(&self, cache: &Arc<RwLock<Cache>>) -> Option<Arc<RwLock<Guild>>> { cache.read().guild(self.guild_id) }
/// Gets all of the channel's invites.
///
@@ -443,16 +456,14 @@ impl GuildChannel {
/// use serenity::model::prelude::*;
/// struct Handler;
///
- /// use serenity::CACHE;
- ///
/// impl EventHandler for Handler {
- /// fn message(&self, _: Context, msg: Message) {
- /// let channel = match CACHE.read().guild_channel(msg.channel_id) {
+ /// fn message(&self, context: Context, msg: Message) {
+ /// let channel = match context.cache.read().guild_channel(msg.channel_id) {
/// Some(channel) => channel,
/// None => return,
/// };
///
- /// let permissions = channel.read().permissions_for(&msg.author).unwrap();
+ /// let permissions = channel.read().permissions_for(&context.cache, &msg.author).unwrap();
///
/// println!("The user's permissions: {:?}", permissions);
/// }
@@ -467,7 +478,6 @@ impl GuildChannel {
/// for demonstrative purposes):
///
/// ```rust,no_run
- /// use serenity::CACHE;
/// use serenity::prelude::*;
/// use serenity::model::prelude::*;
/// use std::fs::File;
@@ -475,15 +485,15 @@ impl GuildChannel {
/// struct Handler;
///
/// impl EventHandler for Handler {
- /// fn message(&self, _: Context, mut msg: Message) {
- /// let channel = match CACHE.read().guild_channel(msg.channel_id) {
+ /// fn message(&self, context: Context, mut msg: Message) {
+ /// let channel = match context.cache.read().guild_channel(msg.channel_id) {
/// Some(channel) => channel,
/// None => return,
/// };
///
- /// let current_user_id = CACHE.read().user.id;
+ /// let current_user_id = context.cache.read().user.id;
/// let permissions =
- /// channel.read().permissions_for(current_user_id).unwrap();
+ /// channel.read().permissions_for(&context.cache, current_user_id).unwrap();
///
/// if !permissions.contains(Permissions::ATTACH_FILES | Permissions::SEND_MESSAGES) {
/// return;
@@ -526,13 +536,13 @@ impl GuildChannel {
/// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[cfg(feature = "cache")]
#[inline]
- pub fn permissions_for<U: Into<UserId>>(&self, user_id: U) -> Result<Permissions> {
- self._permissions_for(user_id.into())
+ pub fn permissions_for<U: Into<UserId>>(&self, cache: &Arc<RwLock<Cache>>, user_id: U) -> Result<Permissions> {
+ self._permissions_for(&cache, user_id.into())
}
#[cfg(feature = "cache")]
- fn _permissions_for(&self, user_id: UserId) -> Result<Permissions> {
- self.guild()
+ fn _permissions_for(&self, cache: &Arc<RwLock<Cache>>, user_id: UserId) -> Result<Permissions> {
+ self.guild(&cache)
.ok_or_else(|| Error::Model(ModelError::GuildNotFound))
.map(|g| g.read().permissions_in(self.id, user_id))
}
@@ -628,13 +638,13 @@ impl GuildChannel {
/// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
/// [`Message`]: struct.Message.html
/// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
- pub fn send_message<F>(&self, f: F) -> Result<Message>
+ pub fn send_message<F>(&self, context: &Context, f: F) -> Result<Message>
where for <'b> F: FnOnce(&'b mut CreateMessage<'b>) -> &'b mut CreateMessage<'b> {
#[cfg(feature = "cache")]
{
let req = Permissions::SEND_MESSAGES;
- if !utils::user_has_perms(self.id, req)? {
+ if !utils::user_has_perms(&context.cache, self.id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs
index 28c00fe..60ee3df 100644
--- a/src/model/channel/message.rs
+++ b/src/model/channel/message.rs
@@ -1,13 +1,19 @@
//! Models relating to Discord channels.
use chrono::{DateTime, FixedOffset};
-use crate::model::prelude::*;
+use crate::{model::prelude::*};
use serde_json::Value;
+#[cfg(feature = "client")]
+use crate::client::Context;
#[cfg(feature = "model")]
use crate::builder::{CreateEmbed, EditMessage};
#[cfg(all(feature = "cache", feature = "model"))]
-use crate::CACHE;
+use crate::cache::Cache;
+#[cfg(all(feature = "cache", feature = "model"))]
+use parking_lot::RwLock;
+#[cfg(all(feature = "cache", feature = "model"))]
+use std::sync::Arc;
#[cfg(all(feature = "cache", feature = "model"))]
use std::fmt::Write;
#[cfg(feature = "model")]
@@ -102,30 +108,30 @@ impl Message {
/// .configure(|c| c.prefix("~"))
/// .cmd("channelname", channel_name));
///
- /// command!(channel_name(_ctx, msg) {
- /// let _ = match msg.channel() {
- /// Some(Channel::Category(c)) => msg.reply(&c.read().name),
- /// Some(Channel::Group(c)) => msg.reply(&c.read().name()),
- /// Some(Channel::Guild(c)) => msg.reply(&c.read().name),
+ /// command!(channel_name(ctx, msg) {
+ /// let _ = match msg.channel(&ctx.cache) {
+ /// Some(Channel::Category(c)) => msg.reply(&ctx, &c.read().name),
+ /// Some(Channel::Group(c)) => msg.reply(&ctx, &c.read().name()),
+ /// Some(Channel::Guild(c)) => msg.reply(&ctx, &c.read().name),
/// Some(Channel::Private(c)) => {
/// let channel = c.read();
/// let user = channel.recipient.read();
///
- /// msg.reply(&format!("DM with {}", user.name.clone()))
+ /// msg.reply(&ctx, &format!("DM with {}", user.name.clone()))
/// },
- /// None => msg.reply("Unknown"),
+ /// None => msg.reply(&ctx, "Unknown"),
/// };
/// });
/// # }
/// ```
#[cfg(feature = "cache")]
#[inline]
- pub fn channel(&self) -> Option<Channel> { CACHE.read().channel(self.channel_id) }
+ pub fn channel(&self, cache: &Arc<RwLock<Cache>>) -> Option<Channel> { cache.read().channel(self.channel_id) }
/// A util function for determining whether this message was sent by someone else, or the
/// bot.
#[cfg(all(feature = "cache", feature = "utils"))]
- pub fn is_own(&self) -> bool { self.author.id == CACHE.read().user.id }
+ pub fn is_own(&self, cache: &Arc<RwLock<Cache>>) -> bool { self.author.id == cache.read().user.id }
/// Deletes the message.
///
@@ -141,12 +147,12 @@ impl Message {
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [`ModelError::InvalidUser`]: ../error/enum.Error.html#variant.InvalidUser
/// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
- pub fn delete(&self) -> Result<()> {
+ pub fn delete(&self, context: &Context) -> Result<()> {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_MESSAGES;
- let is_author = self.author.id == CACHE.read().user.id;
- let has_perms = utils::user_has_perms(self.channel_id, req)?;
+ let is_author = self.author.id == context.cache.read().user.id;
+ let has_perms = utils::user_has_perms(&context.cache, self.channel_id, req)?;
if !is_author && !has_perms {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
@@ -169,12 +175,12 @@ impl Message {
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [`Reaction`]: struct.Reaction.html
/// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
- pub fn delete_reactions(&self) -> Result<()> {
+ pub fn delete_reactions(&self, context: &Context) -> Result<()> {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_MESSAGES;
- if !utils::user_has_perms(self.channel_id, req)? {
+ if !utils::user_has_perms(&context.cache, self.channel_id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -214,11 +220,11 @@ impl Message {
/// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
/// [`EditMessage`]: ../../builder/struct.EditMessage.html
/// [`the limit`]: ../../builder/struct.EditMessage.html#method.content
- pub fn edit<F>(&mut self, f: F) -> Result<()>
+ pub fn edit<F>(&mut self, context: &Context, f: F) -> Result<()>
where F: FnOnce(&mut EditMessage) -> &mut EditMessage {
#[cfg(feature = "cache")]
{
- if self.author.id != CACHE.read().user.id {
+ if self.author.id != context.cache.read().user.id {
return Err(Error::Model(ModelError::InvalidUser));
}
}
@@ -276,7 +282,7 @@ impl Message {
/// Returns message content, but with user and role mentions replaced with
/// names and everyone/here mentions cancelled.
#[cfg(feature = "cache")]
- pub fn content_safe(&self) -> String {
+ pub fn content_safe(&self, cache: &Arc<RwLock<Cache>>) -> String {
let mut result = self.content.clone();
// First replace all user mentions.
@@ -293,7 +299,7 @@ impl Message {
for id in &self.mention_roles {
let mention = id.mention();
- if let Some(role) = id.to_role_cached() {
+ if let Some(role) = id.to_role_cached(&cache) {
result = result.replace(&mention, &format!("@{}", role.name));
} else {
result = result.replace(&mention, "@deleted-role");
@@ -342,8 +348,8 @@ impl Message {
///
/// [`guild_id`]: #method.guild_id
#[cfg(feature = "cache")]
- pub fn guild(&self) -> Option<Arc<RwLock<Guild>>> {
- CACHE.read().guild(self.guild_id?)
+ pub fn guild(&self, cache: &Arc<RwLock<Cache>>) -> Option<Arc<RwLock<Guild>>> {
+ cache.read().guild(self.guild_id?)
}
/// True if message was sent using direct messages.
@@ -360,8 +366,8 @@ impl Message {
///
/// [`Guild::members`]: ../guild/struct.Guild.html#structfield.members
#[cfg(feature = "cache")]
- pub fn member(&self) -> Option<Member> {
- self.guild().and_then(|g| g.read().members.get(&self.author.id).cloned())
+ pub fn member(&self, cache: &Arc<RwLock<Cache>>) -> Option<Member> {
+ self.guild(&cache).and_then(|g| g.read().members.get(&self.author.id).cloned())
}
/// Checks the length of a string to ensure that it is within Discord's
@@ -395,13 +401,13 @@ impl Message {
///
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES.html
- pub fn pin(&self) -> Result<()> {
+ pub fn pin(&self, context: &Context) -> Result<()> {
#[cfg(feature = "cache")]
{
if self.guild_id.is_some() {
let req = Permissions::MANAGE_MESSAGES;
- if !utils::user_has_perms(self.channel_id, req)? {
+ if !utils::user_has_perms(&context.cache, self.channel_id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -426,17 +432,17 @@ impl Message {
/// ../permissions/struct.Permissions.html#associatedconstant.ADD_REACTIONS
/// [permissions]: ../permissions/index.html
#[inline]
- pub fn react<R: Into<ReactionType>>(&self, reaction_type: R) -> Result<()> {
- self._react(&reaction_type.into())
+ pub fn react<R: Into<ReactionType>>(&self, context: &Context, reaction_type: R) -> Result<()> {
+ self._react(&context, &reaction_type.into())
}
- fn _react(&self, reaction_type: &ReactionType) -> Result<()> {
+ fn _react(&self, context: &Context, reaction_type: &ReactionType) -> Result<()> {
#[cfg(feature = "cache")]
{
if self.guild_id.is_some() {
let req = Permissions::ADD_REACTIONS;
- if !utils::user_has_perms(self.channel_id, req)? {
+ if !utils::user_has_perms(&context.cache, self.channel_id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -467,7 +473,7 @@ impl Message {
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
/// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
- pub fn reply(&self, content: &str) -> Result<Message> {
+ pub fn reply(&self, context: &Context, content: &str) -> Result<Message> {
if let Some(length_over) = Message::overflow_length(content) {
return Err(Error::Model(ModelError::MessageTooLong(length_over)));
}
@@ -477,7 +483,7 @@ impl Message {
if self.guild_id.is_some() {
let req = Permissions::SEND_MESSAGES;
- if !utils::user_has_perms(self.channel_id, req)? {
+ if !utils::user_has_perms(&context.cache, self.channel_id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -526,13 +532,13 @@ impl Message {
///
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
- pub fn unpin(&self) -> Result<()> {
+ pub fn unpin(&self, context: &Context) -> Result<()> {
#[cfg(feature = "cache")]
{
if self.guild_id.is_some() {
let req = Permissions::MANAGE_MESSAGES;
- if !utils::user_has_perms(self.channel_id, req)? {
+ if !utils::user_has_perms(&context.cache, self.channel_id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -546,8 +552,8 @@ impl Message {
/// **Note**:
/// If message was sent in a private channel, then the function will return
/// `None`.
- pub fn author_nick(&self) -> Option<String> {
- self.guild_id.as_ref().and_then(|guild_id| self.author.nick_in(*guild_id))
+ pub fn author_nick(&self, context: &Context) -> Option<String> {
+ self.guild_id.as_ref().and_then(|guild_id| self.author.nick_in(&context, *guild_id))
}
pub(crate) fn check_content_length(map: &JsonMap) -> Result<()> {
diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs
index 9c913cb..d21acb2 100644
--- a/src/model/channel/mod.rs
+++ b/src/model/channel/mod.rs
@@ -20,22 +20,29 @@ pub use self::private_channel::*;
pub use self::reaction::*;
pub use self::channel_category::*;
-use crate::internal::RwLockExt;
-use crate::model::prelude::*;
+use crate::{internal::RwLockExt, model::prelude::*};
use serde::de::Error as DeError;
use serde::ser::{SerializeStruct, Serialize, Serializer};
use serde_json;
use super::utils::deserialize_u64;
+#[cfg(feature = "client")]
+use crate::client::Context;
#[cfg(feature = "model")]
use std::fmt::{Display, Formatter, Result as FmtResult};
#[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
-use std::str::FromStr;
+use crate::cache::FromStrAndCache;
#[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
use crate::model::misc::ChannelParseError;
#[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
use crate::utils::parse_channel;
+#[cfg(feature = "cache")]
+use crate::cache::Cache;
+#[cfg(feature = "cache")]
+use std::sync::Arc;
+#[cfg(feature = "cache")]
+use parking_lot::RwLock;
/// A container for any channel.
#[derive(Clone, Debug)]
@@ -71,13 +78,17 @@ impl Channel {
/// Basic usage:
///
/// ```rust,no_run
+ /// # extern crate parking_lot;
/// # extern crate serenity;
/// #
- /// # use self::serenity::model::id::ChannelId;
+ /// # use serenity::{cache::Cache, model::id::ChannelId};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
/// #
/// # #[cfg(feature = "model")]
/// # fn main() {
- /// # let channel = ChannelId(0).to_channel().unwrap();
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// # let channel = ChannelId(0).to_channel_cached(&cache).unwrap();
/// #
/// match channel.group() {
/// Some(group_lock) => {
@@ -113,13 +124,17 @@ impl Channel {
/// Basic usage:
///
/// ```rust,no_run
+ /// # extern crate parking_lot;
/// # extern crate serenity;
/// #
- /// # use self::serenity::model::id::ChannelId;
+ /// # use serenity::{cache::Cache, model::id::ChannelId};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
/// #
/// # #[cfg(feature = "model")]
/// # fn main() {
- /// # let channel = ChannelId(0).to_channel().unwrap();
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// # let channel = ChannelId(0).to_channel_cached(&cache).unwrap();
/// #
/// match channel.guild() {
/// Some(guild_lock) => {
@@ -151,13 +166,17 @@ impl Channel {
/// Basic usage:
///
/// ```rust,no_run
+ /// # extern crate parking_lot;
/// # extern crate serenity;
/// #
- /// # use self::serenity::model::id::ChannelId;
+ /// # use serenity::{cache::Cache, model::id::ChannelId};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
/// #
/// # #[cfg(feature = "model")]
/// # fn main() {
- /// # let channel = ChannelId(0).to_channel().unwrap();
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// # let channel = ChannelId(0).to_channel_cached(&cache).unwrap();
/// #
/// match channel.private() {
/// Some(private_lock) => {
@@ -192,13 +211,17 @@ impl Channel {
/// Basic usage:
///
/// ```rust,no_run
+ /// # extern crate parking_lot;
/// # extern crate serenity;
/// #
- /// # use self::serenity::model::id::ChannelId;
+ /// # use serenity::{cache::Cache, model::id::ChannelId};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
/// #
/// # #[cfg(feature = "model")]
/// # fn main() {
- /// # let channel = ChannelId(0).to_channel().unwrap();
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// # let channel = ChannelId(0).to_channel_cached(&cache).unwrap();
/// #
/// match channel.category() {
/// Some(category_lock) => {
@@ -221,24 +244,27 @@ impl Channel {
/// Deletes the inner channel.
///
+ /// **Note**: If the `cache`-feature is enabled permissions will be checked and upon
+ /// owning the required permissions the HTTP-request will be issued.
+ ///
/// **Note**: There is no real function as _deleting_ a [`Group`]. The
/// closest functionality is leaving it.
///
/// [`Group`]: struct.Group.html
#[cfg(feature = "model")]
- pub fn delete(&self) -> Result<()> {
+ pub fn delete(&self, context: &Context) -> Result<()> {
match *self {
Channel::Group(ref group) => {
let _ = group.read().leave()?;
},
Channel::Guild(ref public_channel) => {
- let _ = public_channel.read().delete()?;
+ let _ = public_channel.read().delete(&context)?;
},
Channel::Private(ref private_channel) => {
let _ = private_channel.read().delete()?;
},
Channel::Category(ref category) => {
- category.read().delete()?;
+ category.read().delete(&context)?;
},
}
@@ -564,12 +590,12 @@ mod test {
}
#[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
-impl FromStr for Channel {
+impl FromStrAndCache for Channel {
type Err = ChannelParseError;
- fn from_str(s: &str) -> StdResult<Self, Self::Err> {
+ fn from_str(cache: &Arc<RwLock<Cache>>, s: &str) -> StdResult<Self, Self::Err> {
match parse_channel(s) {
- Some(x) => match ChannelId(x).to_channel_cached() {
+ Some(x) => match ChannelId(x).to_channel_cached(&cache) {
Some(channel) => Ok(channel),
_ => Err(ChannelParseError::NotPresentInCache),
},
diff --git a/src/model/channel/reaction.rs b/src/model/channel/reaction.rs
index a1df12a..6c5bdeb 100644
--- a/src/model/channel/reaction.rs
+++ b/src/model/channel/reaction.rs
@@ -1,4 +1,4 @@
-use crate::model::prelude::*;
+use crate::{model::prelude::*};
use serde::de::{Deserialize, Error as DeError, MapAccess, Visitor};
use serde::ser::{SerializeMap, Serialize, Serializer};
use std::{
@@ -11,10 +11,11 @@ use std::{
},
str::FromStr
};
+
use crate::internal::prelude::*;
-#[cfg(all(feature = "cache", feature = "model"))]
-use crate::CACHE;
+#[cfg(feature = "client")]
+use crate::client::Context;
#[cfg(feature = "model")]
use crate::http;
@@ -50,8 +51,8 @@ impl Reaction {
///
/// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[inline]
- pub fn channel(&self) -> Result<Channel> {
- self.channel_id.to_channel()
+ pub fn channel(&self, context: &Context) -> Result<Channel> {
+ self.channel_id.to_channel(&context)
}
/// Deletes the reaction, but only if the current user is the user who made
@@ -69,10 +70,10 @@ impl Reaction {
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
/// [permissions]: ../permissions/index.html
- pub fn delete(&self) -> Result<()> {
+ pub fn delete(&self, context: &Context) -> Result<()> {
let user_id = feature_cache! {
{
- let user = if self.user_id == CACHE.read().user.id {
+ let user = if self.user_id == context.cache.read().user.id {
None
} else {
Some(self.user_id.0)
@@ -87,7 +88,7 @@ impl Reaction {
if user.is_some() {
let req = Permissions::MANAGE_MESSAGES;
- if !utils::user_has_perms(self.channel_id, req).unwrap_or(true) {
+ if !utils::user_has_perms(&context.cache, self.channel_id, req).unwrap_or(true) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -122,8 +123,8 @@ impl Reaction {
/// If not - or the user was not found - this will perform a request over
/// the REST API for the user.
#[inline]
- pub fn user(&self) -> Result<User> {
- self.user_id.to_user()
+ pub fn user(&self, context: &Context) -> Result<User> {
+ self.user_id.to_user(&context)
}
/// Retrieves the list of [`User`]s who have reacted to a [`Message`] with a
@@ -326,19 +327,13 @@ impl From<char> for ReactionType {
/// Reacting to a message with an apple:
///
/// ```rust,no_run
- /// # use serenity::model::id::ChannelId;
- /// # use std::error::Error;
- /// #
- /// # fn try_main() -> Result<(), Box<Error>> {
- /// # let message = ChannelId(0).message(0)?;
+ /// # use serenity::{command, model::id::ChannelId};
/// #
- /// message.react('🍎')?;
- /// # Ok(())
- /// # }
+ /// # command!(example(context) {
+ /// # let message = ChannelId(0).message(0)?;
/// #
- /// # fn main() {
- /// # try_main().unwrap();
- /// # }
+ /// message.react(&context, '🍎')?;
+ /// # });
/// ```
fn from(ch: char) -> ReactionType { ReactionType::Unicode(ch.to_string()) }
}
diff --git a/src/model/guild/emoji.rs b/src/model/guild/emoji.rs
index 03c7a7e..dde379e 100644
--- a/src/model/guild/emoji.rs
+++ b/src/model/guild/emoji.rs
@@ -15,7 +15,11 @@ use super::super::ModelError;
#[cfg(all(feature = "cache", feature = "model"))]
use super::super::id::GuildId;
#[cfg(all(feature = "cache", feature = "model"))]
-use crate::{CACHE, http};
+use crate::{cache::Cache, http};
+#[cfg(all(feature = "cache", feature = "model"))]
+use parking_lot::RwLock;
+#[cfg(all(feature = "cache", feature = "model"))]
+use std::sync::Arc;
/// Represents a custom guild emoji, which can either be created using the API,
/// or via an integration. Emojis created using the API only work within the
@@ -52,7 +56,7 @@ impl Emoji {
///
/// **Note**: Only user accounts may use this method.
///
- /// [Manage Emojis]:
+ /// [Manage Emojis]:
/// ../permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
///
/// # Examples
@@ -60,8 +64,14 @@ impl Emoji {
/// Delete a given emoji:
///
/// ```rust,no_run
- /// # use serenity::model::guild::Emoji;
- /// # use serenity::model::id::EmojiId;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
+ /// #
+ /// # use serenity::{cache::Cache, model::{guild::Emoji, id::EmojiId}};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
+ /// #
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
/// #
/// # let mut emoji = Emoji {
/// # animated: false,
@@ -73,14 +83,14 @@ impl Emoji {
/// # };
/// #
/// // assuming emoji has been set already
- /// match emoji.delete() {
+ /// match emoji.delete(&cache) {
/// Ok(()) => println!("Emoji deleted."),
/// Err(_) => println!("Could not delete emoji.")
/// }
/// ```
#[cfg(feature = "cache")]
- pub fn delete(&self) -> Result<()> {
- match self.find_guild_id() {
+ pub fn delete(&self, cache: &Arc<RwLock<Cache>>) -> Result<()> {
+ match self.find_guild_id(&cache) {
Some(guild_id) => http::delete_emoji(guild_id.0, self.id.0),
None => Err(Error::Model(ModelError::ItemMissing)),
}
@@ -99,9 +109,9 @@ impl Emoji {
/// Change the name of an emoji:
///
/// ```rust,no_run
- /// # use serenity::model::guild::Emoji;
- /// # use serenity::model::id::EmojiId;
+ /// # use serenity::{command, model::{guild::Emoji, id::EmojiId}};
/// #
+ /// # command!(example(context) {
/// # let mut emoji = Emoji {
/// # animated: false,
/// # id: EmojiId(7),
@@ -110,14 +120,14 @@ impl Emoji {
/// # require_colons: false,
/// # roles: vec![],
/// # };
- /// #
/// // assuming emoji has been set already
- /// let _ = emoji.edit("blobuwu");
+ /// let _ = emoji.edit(&context.cache, "blobuwu");
/// assert_eq!(emoji.name, "blobuwu");
+ /// # });
/// ```
#[cfg(feature = "cache")]
- pub fn edit(&mut self, name: &str) -> Result<()> {
- match self.find_guild_id() {
+ pub fn edit(&mut self, cache: &Arc<RwLock<Cache>>, name: &str) -> Result<()> {
+ match self.find_guild_id(&cache) {
Some(guild_id) => {
let map = json!({
"name": name,
@@ -145,8 +155,14 @@ impl Emoji {
/// Print the guild id that owns this emoji:
///
/// ```rust,no_run
- /// # use serenity::model::guild::Emoji;
- /// # use serenity::model::id::EmojiId;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
+ /// #
+ /// # use serenity::{cache::Cache, model::{guild::Emoji, id::EmojiId}};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
+ /// #
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
/// #
/// # let mut emoji = Emoji {
/// # animated: false,
@@ -158,13 +174,13 @@ impl Emoji {
/// # };
/// #
/// // assuming emoji has been set already
- /// if let Some(guild_id) = emoji.find_guild_id() {
+ /// if let Some(guild_id) = emoji.find_guild_id(&cache) {
/// println!("{} is owned by {}", emoji.name, guild_id);
/// }
/// ```
#[cfg(feature = "cache")]
- pub fn find_guild_id(&self) -> Option<GuildId> {
- for guild in CACHE.read().guilds.values() {
+ pub fn find_guild_id(&self, cache: &Arc<RwLock<Cache>>) -> Option<GuildId> {
+ for guild in cache.read().guilds.values() {
let guild = guild.read();
if guild.emojis.contains_key(&self.id) {
@@ -182,8 +198,7 @@ impl Emoji {
/// Print the direct link to the given emoji:
///
/// ```rust,no_run
- /// # use serenity::model::guild::Emoji;
- /// # use serenity::model::id::EmojiId;
+ /// # use serenity::model::{guild::Emoji, id::EmojiId};
/// #
/// # let mut emoji = Emoji {
/// # animated: false,
diff --git a/src/model/guild/guild_id.rs b/src/model/guild/guild_id.rs
index c488b52..3146c78 100644
--- a/src/model/guild/guild_id.rs
+++ b/src/model/guild/guild_id.rs
@@ -1,7 +1,9 @@
-use crate::model::prelude::*;
+use crate::{model::prelude::*};
+#[cfg(feature = "client")]
+use crate::client::Context;
#[cfg(all(feature = "cache", feature = "model"))]
-use crate::CACHE;
+use crate::cache::Cache;
#[cfg(feature = "model")]
use crate::builder::{EditGuild, EditMember, EditRole};
#[cfg(feature = "model")]
@@ -407,7 +409,7 @@ impl GuildId {
/// [`Guild`]: ../guild/struct.Guild.html
#[cfg(feature = "cache")]
#[inline]
- pub fn to_guild_cached(self) -> Option<Arc<RwLock<Guild>>> { CACHE.read().guild(self) }
+ pub fn to_guild_cached(self, cache: &Arc<RwLock<Cache>>) -> Option<Arc<RwLock<Guild>>> { cache.read().guild(self) }
/// Requests [`PartialGuild`] over REST API.
///
@@ -456,14 +458,14 @@ impl GuildId {
/// [`Guild`]: ../guild/struct.Guild.html
/// [`Member`]: ../guild/struct.Member.html
#[inline]
- pub fn member<U: Into<UserId>>(&self, user_id: U) -> Result<Member> {
- self._member(user_id.into())
+ pub fn member<U: Into<UserId>>(&self, context: &Context, user_id: U) -> Result<Member> {
+ self._member(&context, user_id.into())
}
- fn _member(&self, user_id: UserId) -> Result<Member> {
+ fn _member(&self, context: &Context, user_id: UserId) -> Result<Member> {
#[cfg(feature = "cache")]
{
- if let Some(member) = CACHE.read().member(self.0, user_id) {
+ if let Some(member) = context.cache.read().member(self.0, user_id) {
return Ok(member);
}
}
@@ -563,7 +565,9 @@ impl GuildId {
/// [`utils::shard_id`]: ../../utils/fn.shard_id.html
#[cfg(all(feature = "cache", feature = "utils"))]
#[inline]
- pub fn shard_id(&self) -> u64 { crate::utils::shard_id(self.0, CACHE.read().shard_count) }
+ pub fn shard_id(&self, cache: &Arc<RwLock<Cache>>) -> u64 {
+ crate::utils::shard_id(self.0, cache.read().shard_count)
+ }
/// Returns the Id of the shard associated with the guild.
///
diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs
index cbda4c3..d969bd0 100644
--- a/src/model/guild/member.rs
+++ b/src/model/guild/member.rs
@@ -1,4 +1,4 @@
-use crate::model::prelude::*;
+use crate::{model::prelude::*};
use chrono::{DateTime, FixedOffset};
use std::fmt::{
Display,
@@ -7,6 +7,8 @@ use std::fmt::{
};
use super::deserialize_sync_user;
+#[cfg(feature = "client")]
+use crate::client::Context;
#[cfg(all(feature = "builder", feature = "cache", feature = "model"))]
use crate::builder::EditMember;
#[cfg(all(feature = "cache", feature = "model"))]
@@ -16,7 +18,7 @@ use std::borrow::Cow;
#[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
use crate::utils::Colour;
#[cfg(all(feature = "cache", feature = "model"))]
-use crate::{CACHE, http, utils};
+use crate::{cache::Cache, http, utils};
/// A trait for allowing both u8 or &str or (u8, &str) to be passed into the `ban` methods in `Guild` and `Member`.
pub trait BanOptions {
@@ -169,8 +171,8 @@ impl Member {
/// Determines the member's colour.
#[cfg(all(feature = "cache", feature = "utils"))]
- pub fn colour(&self) -> Option<Colour> {
- let cache = CACHE.read();
+ pub fn colour(&self, cache: &Arc<RwLock<Cache>>) -> Option<Colour> {
+ let cache = cache.read();
let guild = cache.guilds.get(&self.guild_id)?.read();
let mut roles = self.roles
@@ -191,8 +193,8 @@ impl Member {
/// (This returns the first channel that can be read by the member, if there isn't
/// one returns `None`)
#[cfg(feature = "cache")]
- pub fn default_channel(&self) -> Option<Arc<RwLock<GuildChannel>>> {
- let guild = match self.guild_id.to_guild_cached() {
+ pub fn default_channel(&self, cache: &Arc<RwLock<Cache>>) -> Option<Arc<RwLock<GuildChannel>>> {
+ let guild = match self.guild_id.to_guild_cached(&cache) {
Some(guild) => guild,
None => return None,
};
@@ -257,8 +259,8 @@ impl Member {
/// position. If two or more roles have the same highest position, then the
/// role with the lowest ID is the highest.
#[cfg(feature = "cache")]
- pub fn highest_role_info(&self) -> Option<(RoleId, i64)> {
- let guild = self.guild_id.to_guild_cached()?;
+ pub fn highest_role_info(&self, cache: &Arc<RwLock<Cache>>) -> Option<(RoleId, i64)> {
+ let guild = self.guild_id.to_guild_cached(&cache)?;
let reader = guild.try_read()?;
let mut highest = None;
@@ -315,20 +317,20 @@ impl Member {
/// [`ModelError::GuildNotFound`]: ../error/enum.Error.html#variant.GuildNotFound
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [Kick Members]: ../permissions/struct.Permissions.html#associatedconstant.KICK_MEMBERS
- pub fn kick(&self) -> Result<()> {
+ pub fn kick(&self, context: &Context) -> Result<()> {
#[cfg(feature = "cache")]
{
- let cache = CACHE.read();
+ let locked_cache = context.cache.read();
- if let Some(guild) = cache.guilds.get(&self.guild_id) {
+ if let Some(guild) = locked_cache.guilds.get(&self.guild_id) {
let req = Permissions::KICK_MEMBERS;
let reader = guild.read();
- if !reader.has_perms(req) {
+ if !reader.has_perms(&context.cache, req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
- reader.check_hierarchy(self.user.read().id)?;
+ reader.check_hierarchy(&context.cache, self.user.read().id)?;
}
}
@@ -356,8 +358,8 @@ impl Member {
/// [`ModelError::GuildNotFound`]: ../error/enum.Error.html#variant.GuildNotFound
/// [`ModelError::ItemMissing`]: ../error/enum.Error.html#variant.ItemMissing
#[cfg(feature = "cache")]
- pub fn permissions(&self) -> Result<Permissions> {
- let guild = match self.guild_id.to_guild_cached() {
+ pub fn permissions(&self, cache: &Arc<RwLock<Cache>>) -> Result<Permissions> {
+ let guild = match self.guild_id.to_guild_cached(&cache) {
Some(guild) => guild,
None => return Err(From::from(ModelError::GuildNotFound)),
};
@@ -426,10 +428,10 @@ impl Member {
///
/// If role data can not be found for the member, then `None` is returned.
#[cfg(feature = "cache")]
- pub fn roles(&self) -> Option<Vec<Role>> {
+ pub fn roles(&self, cache: &Arc<RwLock<Cache>>) -> Option<Vec<Role>> {
self
.guild_id
- .to_guild_cached()
+ .to_guild_cached(&cache)
.map(|g| g
.read()
.roles
diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs
index 86cba3d..3ea8d06 100644
--- a/src/model/guild/mod.rs
+++ b/src/model/guild/mod.rs
@@ -17,13 +17,19 @@ pub use self::role::*;
pub use self::audit_log::*;
use chrono::{DateTime, FixedOffset};
-use crate::model::prelude::*;
+use crate::{model::prelude::*};
use serde::de::Error as DeError;
use serde_json;
use super::utils::*;
+#[cfg(feature = "client")]
+use crate::client::Context;
#[cfg(all(feature = "cache", feature = "model"))]
-use crate::CACHE;
+use crate::cache::Cache;
+#[cfg(all(feature = "cache", feature = "model"))]
+use parking_lot::RwLock;
+#[cfg(all(feature = "cache", feature = "model"))]
+use std::sync::Arc;
#[cfg(feature = "model")]
use crate::http;
#[cfg(feature = "model")]
@@ -146,10 +152,10 @@ pub struct Guild {
#[cfg(feature = "model")]
impl Guild {
#[cfg(feature = "cache")]
- fn check_hierarchy(&self, other_user: UserId) -> Result<()> {
- let current_id = CACHE.read().user.id;
+ fn check_hierarchy(&self, cache: &Arc<RwLock<Cache>>, other_user: UserId) -> Result<()> {
+ let current_id = cache.read().user.id;
- if let Some(higher) = self.greater_member_hierarchy(other_user, current_id) {
+ if let Some(higher) = self.greater_member_hierarchy(&cache, other_user, current_id) {
if higher != current_id {
return Err(Error::Model(ModelError::Hierarchy));
}
@@ -189,8 +195,8 @@ impl Guild {
}
#[cfg(feature = "cache")]
- fn has_perms(&self, mut permissions: Permissions) -> bool {
- let user_id = CACHE.read().user.id;
+ fn has_perms(&self, cache: &Arc<RwLock<Cache>>, mut permissions: Permissions) -> bool {
+ let user_id = cache.read().user.id;
let perms = self.member_permissions(user_id);
permissions.remove(perms);
@@ -228,20 +234,20 @@ impl Guild {
/// [`User`]: ../user/struct.User.html
/// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
#[inline]
- pub fn ban<U: Into<UserId>, BO: BanOptions>(&self, user: U, options: &BO) -> Result<()> {
- self._ban(user.into(), options)
+ pub fn ban<U: Into<UserId>, BO: BanOptions>(&self, context: &Context, user: U, options: &BO) -> Result<()> {
+ self._ban(&context, user.into(), options)
}
- fn _ban<BO: BanOptions>(&self, user: UserId, options: &BO) -> Result<()> {
+ fn _ban<BO: BanOptions>(&self, context: &Context, user: UserId, options: &BO) -> Result<()> {
#[cfg(feature = "cache")]
{
let req = Permissions::BAN_MEMBERS;
- if !self.has_perms(req) {
+ if !self.has_perms(&context.cache, req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
- self.check_hierarchy(user)?;
+ self.check_hierarchy(&context.cache, user)?;
}
self.id.ban(user, options)
@@ -259,12 +265,12 @@ impl Guild {
/// [`Ban`]: struct.Ban.html
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
- pub fn bans(&self) -> Result<Vec<Ban>> {
+ pub fn bans(&self, context: &Context) -> Result<Vec<Ban>> {
#[cfg(feature = "cache")]
{
let req = Permissions::BAN_MEMBERS;
- if !self.has_perms(req) {
+ if !self.has_perms(&context.cache, req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -346,13 +352,13 @@ impl Guild {
/// [`Channel`]: ../channel/enum.Channel.html
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [Manage Channels]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
- pub fn create_channel<C>(&self, name: &str, kind: ChannelType, category: C) -> Result<GuildChannel>
+ pub fn create_channel<C>(&self, context: &Context, name: &str, kind: ChannelType, category: C) -> Result<GuildChannel>
where C: Into<Option<ChannelId>> {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_CHANNELS;
- if !self.has_perms(req) {
+ if !self.has_perms(&context.cache, req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -417,13 +423,13 @@ impl Guild {
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [`Role`]: struct.Role.html
/// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
- pub fn create_role<F>(&self, f: F) -> Result<Role>
+ pub fn create_role<F>(&self, context: &Context, f: F) -> Result<Role>
where F: FnOnce(&mut EditRole) -> &mut EditRole {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_ROLES;
- if !self.has_perms(req) {
+ if !self.has_perms(&context.cache, req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -442,10 +448,10 @@ impl Guild {
/// if the current user is not the guild owner.
///
/// [`ModelError::InvalidUser`]: ../error/enum.Error.html#variant.InvalidUser
- pub fn delete(&self) -> Result<PartialGuild> {
+ pub fn delete(&self, context: &Context) -> Result<PartialGuild> {
#[cfg(feature = "cache")]
{
- if self.owner_id != CACHE.read().user.id {
+ if self.owner_id != context.cache.read().user.id {
let req = Permissions::MANAGE_GUILD;
return Err(Error::Model(ModelError::InvalidPermissions(req)));
@@ -519,13 +525,13 @@ impl Guild {
///
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
- pub fn edit<F>(&mut self, f: F) -> Result<()>
+ pub fn edit<F>(&mut self, context: &Context, f: F) -> Result<()>
where F: FnOnce(&mut EditGuild) -> &mut EditGuild {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_GUILD;
- if !self.has_perms(req) {
+ if !self.has_perms(&context.cache, req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -600,12 +606,12 @@ impl Guild {
///
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [Change Nickname]: ../permissions/struct.Permissions.html#associatedconstant.CHANGE_NICKNAME
- pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
+ pub fn edit_nickname(&self, context: &Context, new_nickname: Option<&str>) -> Result<()> {
#[cfg(feature = "cache")]
{
let req = Permissions::CHANGE_NICKNAME;
- if !self.has_perms(req) {
+ if !self.has_perms(&context.cache, req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -673,14 +679,15 @@ impl Guild {
/// [`position`]: struct.Role.html#structfield.position
#[cfg(feature = "cache")]
#[inline]
- pub fn greater_member_hierarchy<T, U>(&self, lhs_id: T, rhs_id: U)
+ pub fn greater_member_hierarchy<T, U>(&self, cache: &Arc<RwLock<Cache>>, lhs_id: T, rhs_id: U)
-> Option<UserId> where T: Into<UserId>, U: Into<UserId> {
- self._greater_member_hierarchy(lhs_id.into(), rhs_id.into())
+ self._greater_member_hierarchy(&cache, lhs_id.into(), rhs_id.into())
}
#[cfg(feature = "cache")]
fn _greater_member_hierarchy(
&self,
+ cache: &Arc<RwLock<Cache>>,
lhs_id: UserId,
rhs_id: UserId,
) -> Option<UserId> {
@@ -697,10 +704,10 @@ impl Guild {
}
let lhs = self.members.get(&lhs_id)?
- .highest_role_info()
+ .highest_role_info(&cache)
.unwrap_or((RoleId(0), 0));
let rhs = self.members.get(&rhs_id)?
- .highest_role_info()
+ .highest_role_info(&cache)
.unwrap_or((RoleId(0), 0));
// If LHS and RHS both have no top position or have the same role ID,
@@ -754,12 +761,12 @@ impl Guild {
///
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
- pub fn invites(&self) -> Result<Vec<RichInvite>> {
+ pub fn invites(&self, context: &Context) -> Result<Vec<RichInvite>> {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_GUILD;
- if !self.has_perms(req) {
+ if !self.has_perms(&context.cache, req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -790,7 +797,9 @@ impl Guild {
/// [`Guild`]: ../guild/struct.Guild.html
/// [`Member`]: struct.Member.html
#[inline]
- pub fn member<U: Into<UserId>>(&self, user_id: U) -> Result<Member> { self.id.member(user_id) }
+ pub fn member<U: Into<UserId>>(&self, context: &Context, user_id: U) -> Result<Member> {
+ self.id.member(&context, user_id)
+ }
/// Gets a list of the guild's members.
///
@@ -1339,12 +1348,12 @@ impl Guild {
/// [`GuildPrune`]: struct.GuildPrune.html
/// [`Member`]: struct.Member.html
/// [Kick Members]: ../permissions/struct.Permissions.html#associatedconstant.KICK_MEMBERS
- pub fn prune_count(&self, days: u16) -> Result<GuildPrune> {
+ pub fn prune_count(&self, context: &Context, days: u16) -> Result<GuildPrune> {
#[cfg(feature = "cache")]
{
let req = Permissions::KICK_MEMBERS;
- if !self.has_perms(req) {
+ if !self.has_perms(&context.cache, req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -1374,7 +1383,7 @@ impl Guild {
/// [`utils::shard_id`]: ../../utils/fn.shard_id.html
#[cfg(all(feature = "cache", feature = "utils"))]
#[inline]
- pub fn shard_id(&self) -> u64 { self.id.shard_id() }
+ pub fn shard_id(&self, cache: &Arc<RwLock<Cache>>) -> u64 { self.id.shard_id(&cache) }
/// Returns the Id of the shard associated with the guild.
///
@@ -1432,12 +1441,12 @@ impl Guild {
/// [`GuildPrune`]: struct.GuildPrune.html
/// [`Member`]: struct.Member.html
/// [Kick Members]: ../permissions/struct.Permissions.html#associatedconstant.KICK_MEMBERS
- pub fn start_prune(&self, days: u16) -> Result<GuildPrune> {
+ pub fn start_prune(&self, context: &Context, days: u16) -> Result<GuildPrune> {
#[cfg(feature = "cache")]
{
let req = Permissions::KICK_MEMBERS;
- if !self.has_perms(req) {
+ if !self.has_perms(&context.cache, req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -1457,12 +1466,12 @@ impl Guild {
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [`User`]: ../user/struct.User.html
/// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
- pub fn unban<U: Into<UserId>>(&self, user_id: U) -> Result<()> {
+ pub fn unban<U: Into<UserId>>(&self, context: &Context, user_id: U) -> Result<()> {
#[cfg(feature = "cache")]
{
let req = Permissions::BAN_MEMBERS;
- if !self.has_perms(req) {
+ if !self.has_perms(&context.cache, req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -1503,11 +1512,9 @@ impl Guild {
///
/// struct Handler;
///
- /// use serenity::CACHE;
- ///
/// impl EventHandler for Handler {
- /// fn message(&self, _: Context, msg: Message) {
- /// if let Some(arc) = msg.guild_id.unwrap().to_guild_cached() {
+ /// fn message(&self, ctx: Context, msg: Message) {
+ /// if let Some(arc) = msg.guild_id.unwrap().to_guild_cached(&ctx.cache) {
/// if let Some(role) = arc.read().role_by_name("role_name") {
/// println!("{:?}", role);
/// }
diff --git a/src/model/guild/partial_guild.rs b/src/model/guild/partial_guild.rs
index 6276db2..8b1be58 100644
--- a/src/model/guild/partial_guild.rs
+++ b/src/model/guild/partial_guild.rs
@@ -1,6 +1,8 @@
-use crate::model::prelude::*;
+use crate::{model::prelude::*};
use super::super::utils::{deserialize_emojis, deserialize_roles};
+#[cfg(feature = "client")]
+use crate::client::Context;
#[cfg(feature = "model")]
use crate::builder::{EditGuild, EditMember, EditRole};
@@ -333,7 +335,9 @@ impl PartialGuild {
///
/// [`Guild`]: struct.Guild.html
/// [`Member`]: struct.Member.html
- pub fn member<U: Into<UserId>>(&self, user_id: U) -> Result<Member> { self.id.member(user_id) }
+ pub fn member<U: Into<UserId>>(&self, context: &Context, user_id: U) -> Result<Member> {
+ self.id.member(&context, user_id)
+ }
/// Gets a list of the guild's members.
///
@@ -380,7 +384,7 @@ impl PartialGuild {
/// [`utils::shard_id`]: ../../utils/fn.shard_id.html
#[cfg(all(feature = "cache", feature = "utils"))]
#[inline]
- pub fn shard_id(&self) -> u64 { self.id.shard_id() }
+ pub fn shard_id(&self, context: &Context) -> u64 { self.id.shard_id(&context.cache) }
/// Returns the Id of the shard associated with the guild.
///
@@ -465,8 +469,6 @@ impl PartialGuild {
///
/// struct Handler;
///
- /// use serenity::CACHE;
- ///
/// impl EventHandler for Handler {
/// fn message(&self, _: Context, msg: Message) {
/// let guild = msg.guild_id.unwrap().to_partial_guild().unwrap();
diff --git a/src/model/guild/role.rs b/src/model/guild/role.rs
index b68803b..404cfcf 100644
--- a/src/model/guild/role.rs
+++ b/src/model/guild/role.rs
@@ -6,10 +6,10 @@ use crate::builder::EditRole;
#[cfg(all(feature = "cache", feature = "model"))]
use crate::internal::prelude::*;
#[cfg(all(feature = "cache", feature = "model"))]
-use crate::{CACHE, Cache, http};
+use crate::{cache::Cache, http};
#[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
-use std::str::FromStr;
+use crate::cache::FromStrAndCache;
#[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
use crate::model::misc::RoleParseError;
#[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
@@ -75,7 +75,9 @@ impl Role {
/// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[cfg(feature = "cache")]
#[inline]
- pub fn delete(&self) -> Result<()> { http::delete_role(self.find_guild()?.0, self.id.0) }
+ pub fn delete(&self, cache: &Arc<RwLock<Cache>>) -> Result<()> {
+ http::delete_role(self.find_guild(&cache)?.0, self.id.0)
+ }
/// Edits a [`Role`], optionally setting its new fields.
///
@@ -85,9 +87,9 @@ impl Role {
///
/// Make a role hoisted:
///
- /// ```rust,no_run
+ /// ```rust,ignore
/// # use serenity::model::id::RoleId;
- /// # let role = RoleId(7).to_role_cached().unwrap();
+ /// # let role = RoleId(7).to_role_cached(&cache).unwrap();
/// // assuming a `role` has already been bound
//
/// role.edit(|mut r| {
@@ -100,8 +102,8 @@ impl Role {
/// [`Role`]: struct.Role.html
/// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[cfg(all(feature = "builder", feature = "cache"))]
- pub fn edit<F: FnOnce(EditRole) -> EditRole>(&self, f: F) -> Result<Role> {
- self.find_guild()
+ pub fn edit<F: FnOnce(EditRole) -> EditRole>(&self, cache: &Arc<RwLock<Cache>>, f: F) -> Result<Role> {
+ self.find_guild(&cache)
.and_then(|guild_id| guild_id.edit_role(self.id, f))
}
@@ -114,8 +116,8 @@ impl Role {
///
/// [`ModelError::GuildNotFound`]: ../error/enum.Error.html#variant.GuildNotFound
#[cfg(feature = "cache")]
- pub fn find_guild(&self) -> Result<GuildId> {
- for guild in CACHE.read().guilds.values() {
+ pub fn find_guild(&self, cache: &Arc<RwLock<Cache>>) -> Result<GuildId> {
+ for guild in cache.read().guilds.values() {
let guild = guild.read();
if guild.roles.contains_key(&RoleId(self.id.0)) {
@@ -178,12 +180,12 @@ impl RoleId {
///
/// [`Role`]: ../guild/struct.Role.html
#[cfg(feature = "cache")]
- pub fn to_role_cached(self) -> Option<Role> {
- self._to_role_cached(&CACHE)
+ pub fn to_role_cached(self, cache: &Arc<RwLock<Cache>>) -> Option<Role> {
+ self._to_role_cached(&cache)
}
#[cfg(feature = "cache")]
- pub(crate) fn _to_role_cached(self, cache: &RwLock<Cache>) -> Option<Role> {
+ pub(crate) fn _to_role_cached(self, cache: &Arc<RwLock<Cache>>) -> Option<Role> {
for guild in cache.read().guilds.values() {
let guild = guild.read();
@@ -211,12 +213,12 @@ impl<'a> From<&'a Role> for RoleId {
}
#[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
-impl FromStr for Role {
+impl FromStrAndCache for Role {
type Err = RoleParseError;
- fn from_str(s: &str) -> StdResult<Self, Self::Err> {
+ fn from_str(cache: &Arc<RwLock<Cache>>, s: &str) -> StdResult<Self, Self::Err> {
match parse_role(s) {
- Some(x) => match RoleId(x).to_role_cached() {
+ Some(x) => match RoleId(x).to_role_cached(&cache) {
Some(role) => Ok(role),
_ => Err(RoleParseError::NotPresentInCache),
},
diff --git a/src/model/invite.rs b/src/model/invite.rs
index 74a46dc..a6f0479 100644
--- a/src/model/invite.rs
+++ b/src/model/invite.rs
@@ -3,6 +3,8 @@
use chrono::{DateTime, FixedOffset};
use super::prelude::*;
+#[cfg(feature = "client")]
+use crate::client::Context;
#[cfg(feature = "model")]
use crate::builder::CreateInvite;
#[cfg(feature = "model")]
@@ -11,6 +13,12 @@ use crate::internal::prelude::*;
use super::{Permissions, utils as model_utils};
#[cfg(feature = "model")]
use crate::{http, utils};
+#[cfg(feature = "cache")]
+use crate::cache::Cache;
+#[cfg(feature = "cache")]
+use parking_lot::RwLock;
+#[cfg(feature = "cache")]
+use std::sync::Arc;
/// Information about an invite code.
///
@@ -70,18 +78,18 @@ impl Invite {
/// [`GuildChannel`]: ../channel/struct.GuildChannel.html
/// [Create Invite]: ../permissions/struct.Permissions.html#associatedconstant.CREATE_INVITE
/// [permission]: ../permissions/index.html
- pub fn create<C, F>(channel_id: C, f: F) -> Result<RichInvite>
+ pub fn create<C, F>(context: &Context, channel_id: C, f: F) -> Result<RichInvite>
where C: Into<ChannelId>, F: FnOnce(CreateInvite) -> CreateInvite {
- Self::_create(channel_id.into(), f)
+ Self::_create(&context, channel_id.into(), f)
}
- fn _create<F>(channel_id: ChannelId, f: F) -> Result<RichInvite>
+ fn _create<F>(context: &Context, channel_id: ChannelId, f: F) -> Result<RichInvite>
where F: FnOnce(CreateInvite) -> CreateInvite {
#[cfg(feature = "cache")]
{
let req = Permissions::CREATE_INVITE;
- if !model_utils::user_has_perms(channel_id, req)? {
+ if !model_utils::user_has_perms(&context.cache, channel_id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -103,12 +111,12 @@ impl Invite {
/// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
/// [permission]: ../permissions/index.html
- pub fn delete(&self) -> Result<Invite> {
+ pub fn delete(&self, context: &Context) -> Result<Invite> {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_GUILD;
- if !model_utils::user_has_perms(self.channel.id, req)? {
+ if !model_utils::user_has_perms(&context.cache, self.channel.id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -195,7 +203,7 @@ impl InviteGuild {
/// [`utils::shard_id`]: ../../utils/fn.shard_id.html
#[cfg(all(feature = "cache", feature = "utils"))]
#[inline]
- pub fn shard_id(&self) -> u64 { self.id.shard_id() }
+ pub fn shard_id(&self, cache: &Arc<RwLock<Cache>>) -> u64 { self.id.shard_id(&cache) }
/// Returns the Id of the shard associated with the guild.
///
@@ -288,12 +296,12 @@ impl RichInvite {
/// [`http::delete_invite`]: ../../http/fn.delete_invite.html
/// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD.html
/// [permission]: ../permissions/index.html
- pub fn delete(&self) -> Result<Invite> {
+ pub fn delete(&self, context: &Context) -> Result<Invite> {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_GUILD;
- if !model_utils::user_has_perms(self.channel.id, req)? {
+ if !model_utils::user_has_perms(&context.cache, self.channel.id, req)? {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
diff --git a/src/model/misc.rs b/src/model/misc.rs
index e2adbcc..5726af6 100644
--- a/src/model/misc.rs
+++ b/src/model/misc.rs
@@ -112,20 +112,6 @@ impl StdError for UserParseError {
}
}
-#[cfg(all(feature = "model", feature = "utils"))]
-impl FromStr for User {
- type Err = UserParseError;
-
- fn from_str(s: &str) -> StdResult<Self, Self::Err> {
- match utils::parse_username(s) {
- Some(x) => UserId(x as u64)
- .to_user()
- .map_err(|e| UserParseError::Rest(Box::new(e))),
- _ => Err(UserParseError::InvalidUsername),
- }
- }
-}
-
macro_rules! impl_from_str {
(id: $($id:tt, $err:ident;)*) => {
$(
diff --git a/src/model/user.rs b/src/model/user.rs
index 80bf40e..923d4d1 100644
--- a/src/model/user.rs
+++ b/src/model/user.rs
@@ -4,11 +4,10 @@ use serde_json;
use std::fmt;
use super::utils::deserialize_u16;
use super::prelude::*;
-use crate::internal::prelude::*;
-use crate::model::misc::Mentionable;
+use crate::{internal::prelude::*, model::misc::Mentionable};
-#[cfg(all(feature = "cache", feature = "model"))]
-use crate::CACHE;
+#[cfg(feature = "client")]
+use crate::client::Context;
#[cfg(feature = "model")]
use crate::builder::{CreateMessage, EditProfile};
#[cfg(feature = "model")]
@@ -22,6 +21,8 @@ use std::fmt::Write;
#[cfg(feature = "model")]
use std::mem;
#[cfg(all(feature = "cache", feature = "model"))]
+use crate::cache::Cache;
+#[cfg(all(feature = "cache", feature = "model"))]
use std::sync::Arc;
#[cfg(feature = "model")]
use crate::utils::{self, VecMap};
@@ -50,10 +51,15 @@ impl CurrentUser {
/// Print out the current user's avatar url if one is set:
///
/// ```rust,no_run
- /// # use serenity::CACHE;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
/// #
- /// # let cache = CACHE.read();
+ /// # use serenity::{cache::Cache, model::prelude::*, prelude::*};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
/// #
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// # let cache = cache.read();
/// // assuming the cache has been unlocked
/// let user = &cache.user;
///
@@ -82,11 +88,9 @@ impl CurrentUser {
/// Change the avatar:
///
/// ```rust,ignore
- /// use serenity::CACHE;
- ///
/// let avatar = serenity::utils::read_image("./avatar.png").unwrap();
///
- /// CACHE.write().user.edit(|p| p.avatar(Some(&avatar)));
+ /// context.cache.write().user.edit(|p| p.avatar(Some(&avatar)));
/// ```
///
/// [`EditProfile`]: ../../builder/struct.EditProfile.html
@@ -131,10 +135,15 @@ impl CurrentUser {
/// Print out the names of all guilds the current user is in:
///
/// ```rust,no_run
- /// # use serenity::CACHE;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
/// #
- /// # let cache = CACHE.read();
+ /// # use serenity::{cache::Cache, model::prelude::*, prelude::*};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
/// #
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// # let cache = cache.read();
/// // assuming the cache has been unlocked
/// let user = &cache.user;
///
@@ -159,9 +168,15 @@ impl CurrentUser {
/// Get the invite url with no permissions set:
///
/// ```rust,no_run
- /// # use serenity::CACHE;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
+ /// #
+ /// # use serenity::{cache::Cache, model::prelude::*, prelude::*};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
/// #
- /// # let mut cache = CACHE.write();
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// # let mut cache = cache.write();
///
/// use serenity::model::Permissions;
///
@@ -182,10 +197,15 @@ impl CurrentUser {
/// Get the invite url with some basic permissions set:
///
/// ```rust,no_run
- /// # use serenity::CACHE;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
/// #
- /// # let mut cache = CACHE.write();
- ///
+ /// # use serenity::{cache::Cache, model::prelude::*, prelude::*};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
+ /// #
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// # let mut cache = cache.write();
/// use serenity::model::Permissions;
///
/// // assuming the cache has been unlocked
@@ -238,10 +258,15 @@ impl CurrentUser {
/// Print out the current user's static avatar url if one is set:
///
/// ```rust,no_run
- /// # use serenity::CACHE;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
/// #
- /// # let cache = CACHE.read();
+ /// # use serenity::{cache::Cache, model::prelude::*, prelude::*};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
/// #
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// # let cache = cache.read();
/// // assuming the cache has been unlocked
/// let user = &cache.user;
///
@@ -262,10 +287,15 @@ impl CurrentUser {
/// Print out the current user's distinct identifier (e.g., Username#1234):
///
/// ```rust,no_run
- /// # use serenity::CACHE;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
/// #
- /// # let cache = CACHE.read();
+ /// # use serenity::{cache::Cache, model::prelude::*, prelude::*};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
/// #
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
+ /// # let cache = cache.read();
/// // assuming the cache has been unlocked
/// println!("The current user's distinct identifier is {}", cache.user.tag());
/// ```
@@ -415,14 +445,13 @@ impl User {
/// # use serenity::model::prelude::*;
/// #
/// use serenity::model::Permissions;
- /// use serenity::CACHE;
///
/// struct Handler;
///
/// impl EventHandler for Handler {
- /// fn message(&self, _: Context, msg: Message) {
+ /// fn message(&self, ctx: Context, msg: Message) {
/// if msg.content == "~help" {
- /// let cache = CACHE.read();
+ /// let cache = ctx.cache.read();
///
/// let url = match cache.user.invite_url(Permissions::empty()) {
/// Ok(v) => v,
@@ -438,18 +467,18 @@ impl User {
/// url,
/// );
///
- /// let dm = msg.author.direct_message(|m| {
+ /// let dm = msg.author.direct_message(&ctx, |m| {
/// m.content(&help)
/// });
///
/// match dm {
/// Ok(_) => {
- /// let _ = msg.react('👌');
+ /// let _ = msg.react(&ctx, '👌');
/// },
/// Err(why) => {
/// println!("Err sending help: {:?}", why);
///
- /// let _ = msg.reply("There was an error DMing you help.");
+ /// let _ = msg.reply(&ctx, "There was an error DMing you help.");
/// },
/// };
/// }
@@ -483,7 +512,7 @@ impl User {
// (AKA: Clippy is wrong and so we have to mark as allowing this lint.)
#[allow(clippy::let_and_return)]
#[cfg(feature = "builder")]
- pub fn direct_message<F>(&self, f: F) -> Result<Message>
+ pub fn direct_message<F>(&self, context: &Context, f: F) -> Result<Message>
where for <'b> F: FnOnce(&'b mut CreateMessage<'b>) -> &'b mut CreateMessage<'b> {
if self.bot {
return Err(Error::Model(ModelError::MessagingBot));
@@ -492,7 +521,7 @@ impl User {
let private_channel_id = feature_cache! {
{
let finding = {
- let cache = CACHE.read();
+ let cache = context.cache.read();
let finding = cache.private_channels
.values()
@@ -545,9 +574,9 @@ impl User {
/// [direct_message]: #method.direct_message
#[cfg(feature = "builder")]
#[inline]
- pub fn dm<F>(&self, f: F) -> Result<Message>
+ pub fn dm<F>(&self, context: &Context, f: F) -> Result<Message>
where for <'b> F: FnOnce(&'b mut CreateMessage<'b>) -> &'b mut CreateMessage<'b> {
- self.direct_message(f)
+ self.direct_message(&context, f)
}
/// Retrieves the URL to the user's avatar, falling back to the default
@@ -585,17 +614,17 @@ impl User {
/// [`Role`]: ../guild/struct.Role.html
/// [`Cache`]: ../../cache/struct.Cache.html
// no-cache would warn on guild_id.
- pub fn has_role<G, R>(&self, guild: G, role: R) -> bool
+ pub fn has_role<G, R>(&self, context: &Context, guild: G, role: R) -> bool
where G: Into<GuildContainer>, R: Into<RoleId> {
- self._has_role(guild.into(), role.into())
+ self._has_role(&context, guild.into(), role.into())
}
- fn _has_role(&self, guild: GuildContainer, role: RoleId) -> bool {
+ fn _has_role(&self, context: &Context, guild: GuildContainer, role: RoleId) -> bool {
match guild {
GuildContainer::Guild(guild) => guild.roles.contains_key(&role),
GuildContainer::Id(_guild_id) => {
feature_cache! {{
- CACHE.read()
+ context.cache.read()
.guilds
.get(&_guild_id)
.map(|g| {
@@ -622,9 +651,12 @@ impl User {
/// out-of-sync:
///
/// ```rust,no_run
- /// # use serenity::prelude::*;
- /// # use serenity::model::prelude::*;
+ /// # extern crate parking_lot;
+ /// # extern crate serenity;
/// #
+ /// # use serenity::{cache::Cache, model::prelude::*, prelude::*};
+ /// # use parking_lot::RwLock;
+ /// # use std::sync::Arc;
/// struct Handler;
///
/// impl EventHandler for Handler {
@@ -635,38 +667,42 @@ impl User {
///
/// let mut client = Client::new("token", Handler).unwrap();
/// #
- /// use serenity::model::id::UserId;
- /// use serenity::CACHE;
+ /// use serenity::{command, model::id::UserId};
/// use std::thread;
/// use std::time::Duration;
///
- /// let special_users = vec![UserId(114941315417899012), UserId(87600987040120832)];
- ///
/// // start a new thread to periodically refresh the special users' data
/// // every 12 hours
+ /// # command!(example(context) {
+ /// # let context = context.clone();
+ ///
/// let handle = thread::spawn(move || {
+ /// let special_users = vec![UserId(114941315417899012), UserId(87600987040120832)];
+ /// # let cache = Arc::new(RwLock::new(Cache::default()));
/// // 12 hours in seconds
/// let duration = Duration::from_secs(43200);
///
/// loop {
/// thread::sleep(duration);
///
- /// let cache = CACHE.read();
+ /// let cache = cache.read();
///
/// for id in &special_users {
+ ///
/// if let Some(user) = cache.user(*id) {
- /// if let Err(why) = user.write().refresh() {
+ ///
+ /// if let Err(why) = user.write().refresh(&context) {
/// println!("Error refreshing {}: {:?}", id, why);
/// }
/// }
/// }
/// }
/// });
- ///
+ /// # });
/// println!("{:?}", client.start());
/// ```
- pub fn refresh(&mut self) -> Result<()> {
- self.id.to_user().map(|replacement| {
+ pub fn refresh(&mut self, context: &Context) -> Result<()> {
+ self.id.to_user(&context).map(|replacement| {
mem::replace(self, replacement);
()
@@ -722,15 +758,15 @@ impl User {
///
/// If none is used, it returns `None`.
#[inline]
- pub fn nick_in<G>(&self, guild_id: G) -> Option<String>
+ pub fn nick_in<G>(&self, context: &Context, guild_id: G) -> Option<String>
where G: Into<GuildId> {
- self._nick_in(guild_id.into())
+ self._nick_in(&context, guild_id.into())
}
- fn _nick_in(&self, guild_id: GuildId) -> Option<String> {
+ fn _nick_in(&self, context: &Context, guild_id: GuildId) -> Option<String> {
#[cfg(feature = "cache")]
{
- guild_id.to_guild_cached().and_then(|guild| {
+ guild_id.to_guild_cached(&context.cache).and_then(|guild| {
guild.read().members.get(&self.id).and_then(|member| member.nick.clone())
})
}
@@ -769,7 +805,7 @@ impl UserId {
/// [`User`]: ../user/struct.User.html
#[cfg(feature = "cache")]
#[inline]
- pub fn to_user_cached(self) -> Option<Arc<RwLock<User>>> { CACHE.read().user(self) }
+ pub fn to_user_cached(self, cache: &Arc<RwLock<Cache>>) -> Option<Arc<RwLock<User>>> { cache.read().user(self) }
/// First attempts to find a [`User`] by its Id in the cache,
/// upon failure requests it via the REST API.
@@ -779,10 +815,10 @@ impl UserId {
///
/// [`User`]: ../user/struct.User.html
#[inline]
- pub fn to_user(self) -> Result<User> {
+ pub fn to_user(self, context: &Context) -> Result<User> {
#[cfg(feature = "cache")]
{
- if let Some(user) = CACHE.read().user(self) {
+ if let Some(user) = context.cache.read().user(self) {
return Ok(user.read().clone());
}
}
diff --git a/src/model/utils.rs b/src/model/utils.rs
index 2399db3..9cb9571 100644
--- a/src/model/utils.rs
+++ b/src/model/utils.rs
@@ -14,7 +14,7 @@ use crate::internal::prelude::*;
#[cfg(all(feature = "cache", feature = "model"))]
use super::permissions::Permissions;
#[cfg(all(feature = "cache", feature = "model"))]
-use crate::CACHE;
+use crate::cache::Cache;
pub fn default_true() -> bool {
true
@@ -206,8 +206,8 @@ pub fn serialize_gen_locked_map<K: Eq + Hash, S: Serializer, V: Serialize>(
}
#[cfg(all(feature = "cache", feature = "model"))]
-pub fn user_has_perms(channel_id: ChannelId, mut permissions: Permissions) -> Result<bool> {
- let cache = CACHE.read();
+pub fn user_has_perms(cache: &Arc<RwLock<Cache>>, channel_id: ChannelId, mut permissions: Permissions) -> Result<bool> {
+ let cache = cache.read();
let current_user = &cache.user;
let channel = match cache.channel(channel_id) {
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 0162010..3b8a24c 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -38,7 +38,7 @@ use std::{
#[cfg(feature = "cache")]
use crate::cache::Cache;
#[cfg(feature = "cache")]
-use crate::CACHE;
+use std::sync::Arc;
/// Converts a HashMap into a final `serde_json::Map` representation.
pub fn hashmap_to_json_map<H, T>(map: HashMap<T, Value, H>)
@@ -467,9 +467,9 @@ pub fn shard_id(guild_id: u64, shard_count: u64) -> u64 { (guild_id >> 22) % sha
/// assert_eq!(1234, utils::with_cache(|cache| cache.user.id));
/// ```
#[cfg(feature = "cache")]
-pub fn with_cache<T, F>(f: F) -> T
+pub fn with_cache<T, F>(cache: &Arc<RwLock<Cache>>, f: F) -> T
where F: Fn(&Cache) -> T {
- let cache = CACHE.read();
+ let cache = cache.read();
f(&cache)
}
@@ -488,9 +488,9 @@ pub fn with_cache<T, F>(f: F) -> T
///
/// [`with_cache`]: #fn.with_cache
#[cfg(feature = "cache")]
-pub fn with_cache_mut<T, F>(mut f: F) -> T
+pub fn with_cache_mut<T, F>(cache: &Arc<RwLock<Cache>>, mut f: F) -> T
where F: FnMut(&mut Cache) -> T {
- let mut cache = CACHE.write();
+ let mut cache = cache.write();
f(&mut cache)
}
@@ -609,7 +609,7 @@ impl Default for ContentSafeOptions {
#[cfg(feature = "cache")]
#[inline]
-fn clean_roles(cache: &RwLock<Cache>, s: &mut String) {
+fn clean_roles(cache: &Arc<RwLock<Cache>>, s: &mut String) {
let mut progress = 0;
while let Some(mut mention_start) = s[progress..].find("<@&") {
@@ -769,28 +769,28 @@ fn clean_users(cache: &RwLock<Cache>, s: &mut String, show_discriminator: bool,
/// Sanitise an `@everyone` mention.
///
/// ```rust
+/// # extern crate serenity;
+/// # extern crate parking_lot;
+/// #
+/// # use std::sync::Arc;
+/// # use serenity::client::Cache;
+/// # use parking_lot::RwLock;
+/// #
+/// # let cache = Arc::new(RwLock::new(Cache::default()));
/// use serenity::utils::{
/// content_safe,
/// ContentSafeOptions,
/// };
///
/// let with_mention = "@everyone";
-/// let without_mention = content_safe(&with_mention, &ContentSafeOptions::default());
+/// let without_mention = content_safe(&cache, &with_mention, &ContentSafeOptions::default());
///
/// assert_eq!("@\u{200B}everyone".to_string(), without_mention);
/// ```
/// [`ContentSafeOptions`]: struct.ContentSafeOptions.html
/// [`Cache`]: ../cache/struct.Cache.html
#[cfg(feature = "cache")]
-pub fn content_safe(s: &str, options: &ContentSafeOptions) -> String {
- let cache = &CACHE;
-
- _content_safe(&cache, s, options)
-}
-
-
-#[cfg(feature = "cache")]
-fn _content_safe(cache: &RwLock<Cache>, s: &str, options: &ContentSafeOptions) -> String {
+pub fn content_safe(cache: &Arc<RwLock<Cache>>, s: &str, options: &ContentSafeOptions) -> String {
let mut s = s.to_string();
if options.clean_role {
@@ -944,7 +944,7 @@ mod test {
slow_mode_rate: 0,
};
- let cache = RwLock::new(Cache::default());
+ let cache = Arc::new(RwLock::new(Cache::default()));
{
let mut cache = cache.try_write().unwrap();
@@ -969,31 +969,31 @@ mod test {
// User mentions
let options = ContentSafeOptions::default();
- assert_eq!(without_user_mentions, _content_safe(&cache, with_user_metions, &options));
+ assert_eq!(without_user_mentions, content_safe(&cache, with_user_metions, &options));
let options = ContentSafeOptions::default();
assert_eq!(format!("@{}#{:04}", user.name, user.discriminator),
- _content_safe(&cache, "<@!100000000000000000>", &options));
+ content_safe(&cache, "<@!100000000000000000>", &options));
let options = ContentSafeOptions::default();
assert_eq!(format!("@{}#{:04}", user.name, user.discriminator),
- _content_safe(&cache, "<@100000000000000000>", &options));
+ content_safe(&cache, "<@100000000000000000>", &options));
let options = options.show_discriminator(false);
assert_eq!(format!("@{}", user.name),
- _content_safe(&cache, "<@!100000000000000000>", &options));
+ content_safe(&cache, "<@!100000000000000000>", &options));
let options = options.show_discriminator(false);
assert_eq!(format!("@{}", user.name),
- _content_safe(&cache, "<@100000000000000000>", &options));
+ content_safe(&cache, "<@100000000000000000>", &options));
let options = options.display_as_member_from(guild.id);
assert_eq!(format!("@{}", member.nick.unwrap()),
- _content_safe(&cache, "<@!100000000000000000>", &options));
+ content_safe(&cache, "<@!100000000000000000>", &options));
let options = options.clean_user(false);
assert_eq!(with_user_metions,
- _content_safe(&cache, with_user_metions, &options));
+ content_safe(&cache, with_user_metions, &options));
// Channel mentions
let with_channel_mentions = "<#> <#deleted-channel> #deleted-channel <#0> \
@@ -1005,11 +1005,11 @@ mod test {
#deleted-channel";
assert_eq!(without_channel_mentions,
- _content_safe(&cache, with_channel_mentions, &options));
+ content_safe(&cache, with_channel_mentions, &options));
let options = options.clean_channel(false);
assert_eq!(with_channel_mentions,
- _content_safe(&cache, with_channel_mentions, &options));
+ content_safe(&cache, with_channel_mentions, &options));
// Role mentions
let with_role_mentions = "<@&> @deleted-role <@&9829> \
@@ -1019,11 +1019,11 @@ mod test {
@ferris-club-member @deleted-role";
assert_eq!(without_role_mentions,
- _content_safe(&cache, with_role_mentions, &options));
+ content_safe(&cache, with_role_mentions, &options));
let options = options.clean_role(false);
assert_eq!(with_role_mentions,
- _content_safe(&cache, with_role_mentions, &options));
+ content_safe(&cache, with_role_mentions, &options));
// Everyone mentions
let with_everyone_mention = "@everyone";
@@ -1031,11 +1031,11 @@ mod test {
let without_everyone_mention = "@\u{200B}everyone";
assert_eq!(without_everyone_mention,
- _content_safe(&cache, with_everyone_mention, &options));
+ content_safe(&cache, with_everyone_mention, &options));
let options = options.clean_everyone(false);
assert_eq!(with_everyone_mention,
- _content_safe(&cache, with_everyone_mention, &options));
+ content_safe(&cache, with_everyone_mention, &options));
// Here mentions
let with_here_mention = "@here";
@@ -1043,10 +1043,10 @@ mod test {
let without_here_mention = "@\u{200B}here";
assert_eq!(without_here_mention,
- _content_safe(&cache, with_here_mention, &options));
+ content_safe(&cache, with_here_mention, &options));
let options = options.clean_here(false);
assert_eq!(with_here_mention,
- _content_safe(&cache, with_here_mention, &options));
+ content_safe(&cache, with_here_mention, &options));
}
}