aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorZeyla Hellyer <[email protected]>2017-02-09 13:34:08 -0800
committerZeyla Hellyer <[email protected]>2017-02-09 13:34:08 -0800
commit0c9ec377aa7281fb3d4bc390c896b426660a5387 (patch)
treea355bda0c0d02d8b67331e0a99090c7b26206cb1 /src
parentRelease v0.1.5 (diff)
downloadserenity-0c9ec377aa7281fb3d4bc390c896b426660a5387.tar.xz
serenity-0c9ec377aa7281fb3d4bc390c896b426660a5387.zip
Optimize caching
Improve the cache by keeping track of new maps, making other maps have `Arc<RwLock>` values, optimizing already-existing methods, and take advantage of new, more efficient retrievals (e.g. simply keying a value from a map rather than iterating over vecs or maps and then itering over another vec). Keep track of two new maps in the cache: - **channels**: a map of all guild channels that exist, so that they can be efficiently found, and so a message's guild can be efficiently found - **users**: a map of all users that exist, so that it can be shared across all members and presences Other cache fields now have `Arc<RwLock>` values: - `groups` - `guilds` - `private_channels` `Cache::unavailable_guilds` is now a `HashSet<GuildId>` instead of a `Vec<GuildId>`. This should slightly optimize removals/insertions for large bots. `ext::cache::ChannelRef` has been removed as it became equivilant in functionality to `model::Channel`. Also, `model::Channel` now has all variant data encased in `Arc<RwLock>`s. E.g., `Channel::Group(Group)` is now `Channel::Group(Arc<RwLock<Group>>)`. Some model struct fields are now wrapped in an `Arc<RwLock>`. These are: - `Group::recipients`: `HashMap<UserId, User>` -> `HashMap<UserId, Arc<RwLock<User>>>` - `Guild::channels`: `HashMap<ChannelId, GuildChannel>` -> `HashMap<ChannelId, Arc<RwLock<GuildChannel>>>` - `Member::user`: `User` -> `Arc<RwLock<User>>` - `PrivateChannel::recipient`: `User` -> `Arc<RwLock<User>>` Some (cache-enabled) event handler signatures have changed to use `Arc<RwLock>`s: - `Client::on_call_delete` - `Client::on_call_update` - `Client::on_guild_delete` - `Client::on_guild_update` Many function signatures have changed: - `Cache::get_call` now returns a `Option<Arc<RwLock<Call>>>` instead of a `Option<&Call>` - `Cache::get_channel` now returns a `Option<Channel>` instead of a `Option<ChannelRef>`. This now also retrieves directly from the `Guild::channels` instead of iterating over guilds' for a guild channel - `Cache::get_guild` now returns a `Option<Arc<RwLock<Guild>>>` instead of a `Option<&Guild>` - `Cache::get_guild_channel` now returns a `Option<Arc<RwLock<GuildChannel>>>` instead of a `Option<&GuildChannel>` - `Cache::get_group` now returns a `Option<Arc<RwLock<Group>>>` instead of a `Option<&Group>` - `Cache::get_member` now returns a `Option<Member>` instead of a `Option<&Member>`, due to guilds being behind a lock themselves - `Cache::get_role` now returns a `Option<Role>` instead of a `Option<&Role>` for the above reason - `Cache::get_user` now returns a `Option<Arc<RwLock<User>>>` instead of a `Option<&User>` - `GuildId::find` now returns a `Option<Arc<RwLock<Guild>>>` instead of a `Option<Guild>` - `UserId::find` now returns a `Option<Arc<RwLock<User>>>` instead of a `Option<User>` - `Member::display_name` now returns a `Cow<String>` instead of a `&str` A new cache method has been added, `Cache::get_private_channel`, to retrieve a `PrivateChannel`. The `Display` formatter for `Channel` has been optimized to not clone.
Diffstat (limited to 'src')
-rw-r--r--src/client/context.rs12
-rw-r--r--src/client/dispatch.rs33
-rw-r--r--src/client/event_store.rs11
-rw-r--r--src/client/gateway/shard.rs4
-rw-r--r--src/client/mod.rs8
-rw-r--r--src/ext/cache/mod.rs667
-rw-r--r--src/ext/framework/mod.rs18
-rw-r--r--src/model/channel.rs68
-rw-r--r--src/model/gateway.rs3
-rw-r--r--src/model/guild.rs154
-rw-r--r--src/model/misc.rs10
-rw-r--r--src/model/mod.rs7
-rw-r--r--src/model/user.rs44
-rw-r--r--src/model/utils.rs21
14 files changed, 634 insertions, 426 deletions
diff --git a/src/client/context.rs b/src/client/context.rs
index bb87c35..ebe6491 100644
--- a/src/client/context.rs
+++ b/src/client/context.rs
@@ -407,12 +407,12 @@ impl Context {
#[cfg(feature="cache")]
{
- match channel_id.get()? {
- Channel::Guild(ref c) if c.kind != ChannelType::Text &&
- c.kind != ChannelType::Voice => {
- return Err(Error::Client(ClientError::UnexpectedChannelType(c.kind)));
- },
- _ => {},
+ if let Channel::Guild(ref channel) = channel_id.get()? {
+ let ch = channel.read().unwrap();
+
+ if ch.kind != ChannelType::Text && ch.kind != ChannelType::Voice {
+ return Err(Error::Client(ClientError::UnexpectedChannelType(ch.kind)));
+ }
}
}
diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs
index e70f280..33267ff 100644
--- a/src/client/dispatch.rs
+++ b/src/client/dispatch.rs
@@ -25,6 +25,22 @@ macro_rules! handler {
}
macro_rules! update {
+ ($method:ident, @$event:expr) => {
+ {
+ #[cfg(feature="cache")]
+ {
+ CACHE.write().unwrap().$method(&mut $event)
+ }
+ }
+ };
+ ($method:ident, @$event:expr, $old:expr) => {
+ {
+ #[cfg(feature="cache")]
+ {
+ CACHE.write().unwrap().$method(&mut $event, $old)
+ }
+ }
+ };
($method:ident, $event:expr) => {
{
#[cfg(feature="cache")]
@@ -107,7 +123,7 @@ fn dispatch_message(context: Context,
}
}
-#[allow(cyclomatic_complexity)]
+#[allow(cyclomatic_complexity, unused_mut)]
fn handle_event(event: Event,
conn: &Arc<Mutex<Shard>>,
data: &Arc<Mutex<ShareMap>>,
@@ -207,8 +223,8 @@ fn handle_event(event: Event,
thread::spawn(move || (handler)(context, event));
}
},
- Event::ChannelRecipientAdd(event) => {
- update!(update_with_channel_recipient_add, event);
+ Event::ChannelRecipientAdd(mut event) => {
+ update!(update_with_channel_recipient_add, @event);
if let Some(handler) = handler!(on_channel_recipient_addition, event_store) {
let context = context(Some(event.channel_id),
@@ -241,8 +257,7 @@ fn handle_event(event: Event,
feature_cache! {{
let before = CACHE.read()
.unwrap()
- .get_channel(event.channel.id())
- .map(|x| x.clone_inner());
+ .get_channel(event.channel.id());
update!(update_with_channel_update, event);
thread::spawn(move || (handler)(context, before, event.channel));
@@ -324,8 +339,8 @@ fn handle_event(event: Event,
thread::spawn(move || (handler)(context, event.guild_id));
}
},
- Event::GuildMemberAdd(event) => {
- update!(update_with_guild_member_add, event);
+ Event::GuildMemberAdd(mut event) => {
+ update!(update_with_guild_member_add, @event);
if let Some(handler) = handler!(on_guild_member_addition, event_store) {
let context = context(None, conn, data, login_type);
@@ -516,8 +531,8 @@ fn handle_event(event: Event,
thread::spawn(move || (handler)(context, event.presences));
}
},
- Event::PresenceUpdate(event) => {
- update!(update_with_presence_update, event);
+ Event::PresenceUpdate(mut event) => {
+ update!(update_with_presence_update, @event);
if let Some(handler) = handler!(on_presence_update, event_store) {
let context = context(None, conn, data, login_type);
diff --git a/src/client/event_store.rs b/src/client/event_store.rs
index 7cc7e81..f63c03a 100644
--- a/src/client/event_store.rs
+++ b/src/client/event_store.rs
@@ -14,6 +14,9 @@ use ::model::event::{
};
use ::model::*;
+#[cfg(feature="cache")]
+use std::sync::RwLock;
+
#[cfg(not(feature="cache"))]
use ::model::event::{
CallUpdateEvent,
@@ -44,11 +47,11 @@ use ::model::event::{
pub struct EventStore {
pub on_call_create: Option<Arc<Fn(Context, Call) + Send + Sync + 'static>>,
#[cfg(feature="cache")]
- pub on_call_delete: Option<Arc<Fn(Context, ChannelId, Option<Call>) + Send + Sync + 'static>>,
+ pub on_call_delete: Option<Arc<Fn(Context, ChannelId, Option<Arc<RwLock<Call>>>) + Send + Sync + 'static>>,
#[cfg(not(feature="cache"))]
pub on_call_delete: Option<Arc<Fn(Context, ChannelId) + Send + Sync + 'static>>,
#[cfg(feature="cache")]
- pub on_call_update: Option<Arc<Fn(Context, Option<Call>, Option<Call>) + Send + Sync + 'static>>,
+ pub on_call_update: Option<Arc<Fn(Context, Option<Arc<RwLock<Call>>>, Option<Arc<RwLock<Call>>>) + Send + Sync + 'static>>,
#[cfg(not(feature="cache"))]
pub on_call_update: Option<Arc<Fn(Context, CallUpdateEvent) + Send + Sync + 'static>>,
pub on_channel_create: Option<Arc<Fn(Context, Channel) + Send + Sync + 'static>>,
@@ -67,7 +70,7 @@ pub struct EventStore {
pub on_guild_ban_removal: Option<Arc<Fn(Context, GuildId, User) + Send + Sync + 'static>>,
pub on_guild_create: Option<Arc<Fn(Context, Guild) + Send + Sync + 'static>>,
#[cfg(feature="cache")]
- pub on_guild_delete: Option<Arc<Fn(Context, PartialGuild, Option<Guild>) + Send + Sync + 'static>>,
+ pub on_guild_delete: Option<Arc<Fn(Context, PartialGuild, Option<Arc<RwLock<Guild>>>) + Send + Sync + 'static>>,
#[cfg(not(feature="cache"))]
pub on_guild_delete: Option<Arc<Fn(Context, PartialGuild) + Send + Sync + 'static>>,
pub on_guild_emojis_update: Option<Arc<Fn(Context, GuildId, HashMap<EmojiId, Emoji>) + Send + Sync + 'static>>,
@@ -94,7 +97,7 @@ pub struct EventStore {
pub on_guild_sync: Option<Arc<Fn(Context, GuildSyncEvent) + Send + Sync + 'static>>,
pub on_guild_unavailable: Option<Arc<Fn(Context, GuildId) + Send + Sync + 'static>>,
#[cfg(feature="cache")]
- pub on_guild_update: Option<Arc<Fn(Context, Option<Guild>, PartialGuild) + Send + Sync + 'static>>,
+ pub on_guild_update: Option<Arc<Fn(Context, Option<Arc<RwLock<Guild>>>, PartialGuild) + Send + Sync + 'static>>,
#[cfg(not(feature="cache"))]
pub on_guild_update: Option<Arc<Fn(Context, PartialGuild) + Send + Sync + 'static>>,
pub on_message: Option<Arc<Fn(Context, Message) + Send + Sync + 'static>>,
diff --git a/src/client/gateway/shard.rs b/src/client/gateway/shard.rs
index 283c7cd..8902b0b 100644
--- a/src/client/gateway/shard.rs
+++ b/src/client/gateway/shard.rs
@@ -668,8 +668,7 @@ impl Shard {
match game.as_ref() {
Some(game) => {
- object.insert_object("game", move |o| o
- .insert("name", &game.name))
+ object.insert_object("game", move |o| o.insert("name", &game.name))
},
None => object.insert("game", Value::Null),
}
@@ -682,6 +681,7 @@ impl Shard {
{
let mut cache = CACHE.write().unwrap();
let current_user_id = cache.user.id;
+
cache.presences.get_mut(&current_user_id).map(|presence| {
presence.game = game.clone();
presence.last_modified = Some(now);
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 5ef770a..d0593fe 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -868,7 +868,7 @@ impl Client {
///
/// [`CallDelete`]: ../model/event/enum.Event.html#variant.CallDelete
pub fn on_call_delete<F>(&mut self, handler: F)
- where F: Fn(Context, ChannelId, Option<Call>) + Send + Sync + 'static {
+ where F: Fn(Context, ChannelId, Option<Arc<RwLock<Call>>>) + Send + Sync + 'static {
self.event_store.write()
.unwrap()
.on_call_delete = Some(Arc::new(handler));
@@ -878,7 +878,7 @@ impl Client {
///
/// [`CallUpdate`]: ../model/event/enum.Event.html#variant.CallUpdate
pub fn on_call_update<F>(&mut self, handler: F)
- where F: Fn(Context, Option<Call>, Option<Call>) + Send + Sync + 'static {
+ where F: Fn(Context, Option<Arc<RwLock<Call>>>, Option<Arc<RwLock<Call>>>) + Send + Sync + 'static {
self.event_store.write()
.unwrap()
.on_call_update = Some(Arc::new(handler));
@@ -910,7 +910,7 @@ impl Client {
/// [`Role`]: ../model/struct.Role.html
/// [`Cache`]: ../ext/cache/struct.Cache.html
pub fn on_guild_delete<F>(&mut self, handler: F)
- where F: Fn(Context, PartialGuild, Option<Guild>) + Send + Sync + 'static {
+ where F: Fn(Context, PartialGuild, Option<Arc<RwLock<Guild>>>) + Send + Sync + 'static {
self.event_store.write()
.unwrap()
.on_guild_delete = Some(Arc::new(handler));
@@ -977,7 +977,7 @@ impl Client {
///
/// [`GuildUpdate`]: ../model/event/enum.Event.html#variant.GuildUpdate
pub fn on_guild_update<F>(&mut self, handler: F)
- where F: Fn(Context, Option<Guild>, PartialGuild) + Send + Sync + 'static {
+ where F: Fn(Context, Option<Arc<RwLock<Guild>>>, PartialGuild) + Send + Sync + 'static {
self.event_store.write()
.unwrap()
.on_guild_update = Some(Arc::new(handler));
diff --git a/src/ext/cache/mod.rs b/src/ext/cache/mod.rs
index b6a32cd..d0906bc 100644
--- a/src/ext/cache/mod.rs
+++ b/src/ext/cache/mod.rs
@@ -76,8 +76,9 @@
//! [`rest`]: ../../client/rest/index.html
use std::collections::hash_map::Entry;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
use std::default::Default;
+use std::sync::{Arc, RwLock};
use std::mem;
use ::model::*;
use ::model::event::*;
@@ -124,13 +125,24 @@ pub struct Cache {
/// [`Group`]: ../../model/struct.Group.html
/// [`PrivateChannel`]: ../../model/struct.PrivateChannel.html
/// [special cases]: index.html#special-cases-in-the-cache
- pub calls: HashMap<ChannelId, Call>,
+ pub calls: HashMap<ChannelId, Arc<RwLock<Call>>>,
+ /// A map of channels in [`Guild`]s that the current user has received data
+ /// for.
+ ///
+ /// When a [`Event::GuildDelete`] or [`Event::GuildUnavailable`] is
+ /// received and processed by the cache, the relevant channels are also
+ /// removed from this map.
+ ///
+ /// [`Event::GuildDelete`]: ../../model/event/struct.GuildDeleteEvent.html
+ /// [`Event::GuildUnavailable`]: ../../model/event/struct.GuildUnavailableEvent.html
+ /// [`Guild`]: ../../model/struct.Guild.html
+ pub channels: HashMap<ChannelId, Arc<RwLock<GuildChannel>>>,
/// A map of the groups that the current user is in.
///
/// For bot users this will always be empty, except for in [special cases].
///
/// [special cases]: index.html#special-cases-in-the-cache
- pub groups: HashMap<ChannelId, Group>,
+ pub groups: HashMap<ChannelId, Arc<RwLock<Group>>>,
/// Settings specific to a guild.
///
/// This will always be empty for bot users.
@@ -140,7 +152,7 @@ pub struct Cache {
///
/// [`Emoji`]: ../../model/struct.Emoji.html
/// [`Role`]: ../../model/struct.Role.html
- pub guilds: HashMap<GuildId, Guild>,
+ pub guilds: HashMap<GuildId, Arc<RwLock<Guild>>>,
/// A map of notes that a user has made for individual users.
///
/// An empty note is equivalent to having no note, and creating an empty
@@ -154,7 +166,7 @@ pub struct Cache {
pub presences: HashMap<UserId, Presence>,
/// A map of direct message channels that the current user has open with
/// other users.
- pub private_channels: HashMap<ChannelId, PrivateChannel>,
+ pub private_channels: HashMap<ChannelId, Arc<RwLock<PrivateChannel>>>,
/// A map of relationships that the current user has with other users.
///
/// For bot users this will always be empty, except for in [special cases].
@@ -172,7 +184,7 @@ pub struct Cache {
///
/// [`Event::GuildCreate`]: ../../model/enum.Event.html#variant.GuildCreate
/// [`Event::GuildUnavailable`]: ../../model/enum.Event.html#variant.GuildUnavailable
- pub unavailable_guilds: Vec<GuildId>,
+ pub unavailable_guilds: HashSet<GuildId>,
/// The current user "logged in" and for which events are being received
/// for.
///
@@ -184,6 +196,34 @@ pub struct Cache {
/// [`CurrentUser`]: ../../model/struct.CurrentUser.html
/// [`User`]: ../../model/struct.User.html
pub user: CurrentUser,
+ /// A map of users that the current user sees.
+ ///
+ /// Users are added to - and updated from - this map via the following
+ /// received events:
+ ///
+ /// - [`ChannelRecipientAdd`][`ChannelRecipientAddEvent`]
+ /// - [`GuildMemberAdd`][`GuildMemberAddEvent`]
+ /// - [`GuildMemberRemove`][`GuildMemberRemoveEvent`]
+ /// - [`GuildMembersChunk`][`GuildMembersChunkEvent`]
+ /// - [`GuildSync`][`GuildSyncEvent`]
+ /// - [`PresenceUpdate`][`PresenceUpdateEvent`]
+ /// - [`Ready`][`ReadyEvent`]
+ /// - [`RelationshipAdd`][`RelationshipAddEvent`]
+ ///
+ /// Note, however, that users are _not_ removed from the map on removal
+ /// events such as [`GuildMemberRemove`][`GuildMemberRemoveEvent`], as other
+ /// structs such as members or recipients may still exist.
+ ///
+ /// [`ChannelRecipientAddEvent`]: ../../model/event/struct.ChannelRecipientAddEvent.html
+ /// [`GuildMemberAddEvent`]: ../../model/event/struct.GuildMemberAddEvent.html
+ /// [`GuildMemberRemoveEvent`]: ../../model/event/struct.GuildMemberRemoveEvent.html
+ /// [`GuildMemberUpdateEvent`]: ../../model/event/struct.GuildMemberUpdateEvent.html
+ /// [`GuildMembersChunkEvent`]: ../../model/event/struct.GuildMembersChunkEvent.html
+ /// [`GuildSyncEvent`]: ../../model/event/struct.GuildSyncEvent.html
+ /// [`PresenceUpdateEvent`]: ../../model/event/struct.PresenceUpdateEvent.html
+ /// [`Ready`]: ../../model/event/struct.ReadyEvent.html
+ /// [`RelationshipAdd`][`RelationshipAddEvent`]
+ pub users: HashMap<UserId, Arc<RwLock<User>>>,
}
impl Cache {
@@ -193,16 +233,17 @@ impl Cache {
/// _member_s that have not had data downloaded. A single [`User`] may have
/// multiple associated member objects that have not been received.
///
- /// This can be used in combination with [`Shard::sync_guilds`], and can be
+ /// This can be used in combination with [`Shard::chunk_guilds`], and can be
/// used to determine how many members have not yet been downloaded.
///
/// [`Member`]: ../../model/struct.Member.html
- /// [`Shard::sync_guilds`]: ../../client/gateway/struct.Shard.html#method.sync_guilds
/// [`User`]: ../../model/struct.User.html
pub fn unknown_members(&self) -> u64 {
let mut total = 0;
for guild in self.guilds.values() {
+ let guild = guild.read().unwrap();
+
let members = guild.members.len() as u64;
if guild.member_count > members {
@@ -243,73 +284,77 @@ impl Cache {
pub fn all_guilds(&self) -> Vec<GuildId> {
self.guilds
.values()
- .map(|g| g.id)
+ .map(|g| g.read().unwrap().id)
.chain(self.unavailable_guilds.iter().cloned())
.collect()
}
- #[doc(hidden)]
- pub fn __download_members(&mut self) -> Vec<GuildId> {
- self.guilds
- .values_mut()
- .filter(|guild| guild.large)
- .map(|ref mut guild| {
- guild.members.clear();
-
- guild.id
- })
- .collect::<Vec<GuildId>>()
- }
-
/// Retrieves a reference to a [`Call`] from the cache based on the
/// associated [`Group`]'s channel Id.
///
/// [`Call`]: ../../model/struct.Call.html
/// [`Group`]: ../../model/struct.Group.html
- pub fn get_call<C: Into<ChannelId>>(&self, group_id: C) -> Option<&Call> {
- self.calls.get(&group_id.into())
+ #[inline]
+ pub fn get_call<C: Into<ChannelId>>(&self, group_id: C) -> Option<Arc<RwLock<Call>>> {
+ self.calls.get(&group_id.into()).cloned()
}
/// Retrieves a [`Channel`] from the cache based on the given Id.
///
- /// This will search the [`groups`] map, the [`private_channels`] map, and
- /// then the map of [`guilds`] to find the channel.
+ /// This will search the [`channels`] map, the [`private_channels`] map, and
+ /// then the map of [`groups`] to find the channel.
+ ///
+ /// If you know what type of channel you're looking for, you should instead
+ /// manually retrieve from one of the respective maps or methods:
+ ///
+ /// - [`GuildChannel`]: [`get_guild_channel`] or [`channels`]
+ /// - [`PrivateChannel`]: [`get_private_channel`] or [`private_channels`]
+ /// - [`Group`]: [`get_group`] or [`groups`]
///
/// [`Channel`]: ../../model/enum.Channel.html
+ /// [`Group`]: ../../model/struct.Group.html
/// [`Guild`]: ../../model/struct.Guild.html
+ /// [`channels`]: #structfield.channels
+ /// [`get_group`]: #method.get_group
+ /// [`get_guild_channel`]: #method.get_guild_channel
+ /// [`get_private_channel`]: #method.get_private_channel
/// [`groups`]: #structfield.groups
/// [`private_channels`]: #structfield.private_channels
- /// [`guilds`]: #structfield.guilds
- pub fn get_channel<C: Into<ChannelId>>(&self, id: C) -> Option<ChannelRef> {
+ pub fn get_channel<C: Into<ChannelId>>(&self, id: C) -> Option<Channel> {
let id = id.into();
- if let Some(private_channel) = self.private_channels.get(&id) {
- return Some(ChannelRef::Private(private_channel));
+ if let Some(channel) = self.channels.get(&id) {
+ return Some(Channel::Guild(channel.clone()));
}
- if let Some(group) = self.groups.get(&id) {
- return Some(ChannelRef::Group(group));
+ if let Some(private_channel) = self.private_channels.get(&id) {
+ return Some(Channel::Private(private_channel.clone()));
}
- for guild in self.guilds.values() {
- for channel in guild.channels.values() {
- if channel.id == id {
- return Some(ChannelRef::Guild(channel));
- }
- }
+ if let Some(group) = self.groups.get(&id) {
+ return Some(Channel::Group(group.clone()));
}
None
}
- /// Retrieves a reference to a guild from the cache based on the given Id.
- pub fn get_guild<G: Into<GuildId>>(&self, id: G) -> Option<&Guild> {
- self.guilds.get(&id.into())
+ /// Retrieves a guild from the cache based on the given Id.
+ ///
+ /// The only advantage of this method is that you can pass in anything that
+ /// is indirectly a [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/struct.GuildId.html
+ #[inline]
+ pub fn get_guild<G: Into<GuildId>>(&self, id: G) -> Option<Arc<RwLock<Guild>>> {
+ self.guilds.get(&id.into()).cloned()
}
/// Retrieves a reference to a [`Guild`]'s channel. Unlike [`get_channel`],
/// this will only search guilds for the given channel.
///
+ /// The only advantage of this method is that you can pass in anything that
+ /// is indirectly a [`ChannelId`].
+ ///
/// # Examples
///
/// Getting a guild's channel via the Id of the message received through a
@@ -332,31 +377,34 @@ impl Cache {
/// };
/// ```
///
+ /// [`ChannelId`]: ../../model/struct.ChannelId.html
/// [`Client::on_message`]: ../../client/struct.Client.html#method.on_message
/// [`Guild`]: ../../model/struct.Guild.html
/// [`get_channel`]: #method.get_channel
- pub fn get_guild_channel<C: Into<ChannelId>>(&self, id: C) -> Option<&GuildChannel> {
- let id = id.into();
-
- for guild in self.guilds.values() {
- if let Some(channel) = guild.channels.get(&id) {
- return Some(channel);
- }
- }
-
- None
+ #[inline]
+ pub fn get_guild_channel<C: Into<ChannelId>>(&self, id: C)
+ -> Option<Arc<RwLock<GuildChannel>>> {
+ self.channels.get(&id.into()).cloned()
}
/// Retrieves a reference to a [`Group`] from the cache based on the given
/// associated channel Id.
///
+ /// The only advantage of this method is that you can pass in anything that
+ /// is indirectly a [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/struct.ChannelId.html
/// [`Group`]: ../../model/struct.Group.html
- pub fn get_group<C: Into<ChannelId>>(&self, id: C) -> Option<&Group> {
- self.groups.get(&id.into())
+ #[inline]
+ pub fn get_group<C: Into<ChannelId>>(&self, id: C) -> Option<Arc<RwLock<Group>>> {
+ self.groups.get(&id.into()).cloned()
}
- /// Retrieves a reference to a [`Guild`]'s member from the cache based on
- /// the guild's and user's given Ids.
+ /// Retrieves a [`Guild`]'s member from the cache based on the guild's and
+ /// user's given Ids.
+ ///
+ /// **Note**: This will clone the entire member. Instead, retrieve the guild
+ /// and retrieve from the guild's [`members`] map to avoid this.
///
/// # Examples
///
@@ -398,65 +446,80 @@ impl Cache {
///
/// [`Client::on_message`]: ../../client/struct.Client.html#method.on_message
/// [`Guild`]: ../../model/struct.Guild.html
- pub fn get_member<G, U>(&self, guild_id: G, user_id: U) -> Option<&Member>
+ /// [`members`]: ../../model/struct.Guild.html#structfield.members
+ pub fn get_member<G, U>(&self, guild_id: G, user_id: U) -> Option<Member>
where G: Into<GuildId>, U: Into<UserId> {
self.guilds
.get(&guild_id.into())
.map(|guild| {
- guild.members.get(&user_id.into())
+ guild.write().unwrap().members.get(&user_id.into()).cloned()
}).and_then(|x| match x {
Some(x) => Some(x),
None => None,
})
}
- /// Retrieves a reference to a `User` based on appearance in
- /// the first server they are in.
- pub fn get_user<U: Into<UserId>>(&self, user_id: U) -> Option<&User> {
- let user_id = user_id.into();
-
- for guild in self.guilds.values() {
- if let Some(member) = guild.members.get(&user_id) {
- return Some(&member.user);
- }
- }
-
- None
+ /// Retrieves a [`PrivateChannel`] from the cache's [`private_channels`]
+ /// map, if it exists.
+ ///
+ /// The only advantage of this method is that you can pass in anything that
+ /// is indirectly a [`ChannelId`].
+ #[inline]
+ pub fn get_private_channel<C: Into<ChannelId>>(&self, channel_id: C)
+ -> Option<Arc<RwLock<PrivateChannel>>> {
+ self.private_channels.get(&channel_id.into()).cloned()
}
- /// Retrieves a reference to a [`Guild`]'s role by their Ids.
+ /// Retrieves a [`Guild`]'s role by their Ids.
+ ///
+ /// **Note**: This will clone the entire role. Instead, retrieve the guild
+ /// and retrieve from the guild's [`roles`] map to avoid this.
///
/// [`Guild`]: ../../model/struct.Guild.html
- pub fn get_role<G, R>(&self, guild_id: G, role_id: R) -> Option<&Role>
+ /// [`roles`]: ../../model/struct.Guild.html#structfield.roles
+ pub fn get_role<G, R>(&self, guild_id: G, role_id: R) -> Option<Role>
where G: Into<GuildId>, R: Into<RoleId> {
- if let Some(guild) = self.guilds.get(&guild_id.into()) {
- guild.roles.get(&role_id.into())
- } else {
- None
- }
+ self.guilds.get(&guild_id.into())
+ .map(|g| g.read().unwrap().roles.get(&role_id.into()).cloned())
+ .and_then(|x| match x {
+ Some(x) => Some(x),
+ None => None,
+ })
+ }
+
+ /// Retrieves a `User` from the cache's [`users`] map, if it exists.
+ ///
+ /// The only advantage of this method is that you can pass in anything that
+ /// is indirectly a [`UserId`].
+ ///
+ /// [`UserId`]: ../../model/struct.UserId.html
+ /// [`users`]: #structfield.users
+ #[inline]
+ pub fn get_user<U: Into<UserId>>(&self, user_id: U) -> Option<Arc<RwLock<User>>> {
+ self.users.get(&user_id.into()).cloned()
}
#[doc(hidden)]
pub fn update_with_call_create(&mut self, event: &CallCreateEvent) {
match self.calls.entry(event.call.channel_id) {
Entry::Vacant(e) => {
- e.insert(event.call.clone());
+ e.insert(Arc::new(RwLock::new(event.call.clone())));
},
Entry::Occupied(mut e) => {
- e.get_mut().clone_from(&event.call);
+ *e.get_mut() = Arc::new(RwLock::new(event.call.clone()));
},
}
}
#[doc(hidden)]
pub fn update_with_call_delete(&mut self, event: &CallDeleteEvent)
- -> Option<Call> {
+ -> Option<Arc<RwLock<Call>>> {
self.calls.remove(&event.channel_id)
}
#[doc(hidden)]
pub fn update_with_call_update(&mut self, event: &CallUpdateEvent, old: bool)
- -> Option<Call> {
+ -> Option<Arc<RwLock<Call>>> {
let item = if old {
self.calls.get(&event.channel_id).cloned()
} else {
@@ -466,6 +529,8 @@ impl Cache {
self.calls
.get_mut(&event.channel_id)
.map(|call| {
+ let mut call = call.write().unwrap();
+
call.region.clone_from(&event.region);
call.ringing.clone_from(&event.ringing);
});
@@ -474,24 +539,40 @@ impl Cache {
}
#[doc(hidden)]
- pub fn update_with_channel_create(&mut self, event: &ChannelCreateEvent)
- -> Option<Channel> {
+ pub fn update_with_channel_create(&mut self, event: &ChannelCreateEvent) -> Option<Channel> {
match event.channel {
Channel::Group(ref group) => {
- let ch = self.groups.insert(group.channel_id, group.clone());
+ let group = group.clone();
- ch.map(Channel::Group)
- },
- Channel::Private(ref channel) => {
- let ch = self.private_channels.insert(channel.id, channel.clone());
+ let channel_id = {
+ let writer = group.write().unwrap();
- ch.map(Channel::Private)
+ for (recipient_id, recipient) in &mut group.write().unwrap().recipients {
+ self.update_user_entry(&recipient.read().unwrap());
+
+ *recipient = self.users[recipient_id].clone();
+ }
+
+ writer.channel_id
+ };
+
+ let ch = self.groups.insert(channel_id, group);
+
+ ch.map(Channel::Group)
},
Channel::Guild(ref channel) => {
+ let (guild_id, channel_id) = {
+ let channel = channel.read().unwrap();
+
+ (channel.guild_id, channel.id)
+ };
+
+ self.channels.insert(channel_id, channel.clone());
+
let ch = self.guilds
- .get_mut(&channel.guild_id)
+ .get_mut(&guild_id)
.map(|guild| {
- guild.channels.insert(channel.id, channel.clone())
+ guild.write().unwrap().channels.insert(channel_id, channel.clone())
});
match ch {
@@ -499,24 +580,48 @@ impl Cache {
_ => None,
}
},
+ Channel::Private(ref channel) => {
+ let channel = channel.clone();
+
+ let mut channel_writer = channel.write().unwrap();
+
+ let user_id = {
+ let user_reader = channel_writer.recipient.read().unwrap();
+
+ self.update_user_entry(&user_reader);
+
+ user_reader.id
+ };
+
+ channel_writer.recipient = self.users[&user_id].clone();
+
+ let ch = self.private_channels.insert(channel.read().unwrap().id, channel.clone());
+ ch.map(Channel::Private)
+ },
}
}
#[doc(hidden)]
- pub fn update_with_channel_delete(&mut self, event: &ChannelDeleteEvent)
- -> Option<Channel> {
+ pub fn update_with_channel_delete(&mut self, event: &ChannelDeleteEvent) -> Option<Channel> {
match event.channel {
Channel::Group(ref group) => {
- self.groups.remove(&group.channel_id).map(Channel::Group)
+ self.groups.remove(&group.read().unwrap().channel_id).map(Channel::Group)
},
Channel::Private(ref channel) => {
- self.private_channels.remove(&channel.id)
- .map(Channel::Private)
+ self.private_channels.remove(&channel.read().unwrap().id).map(Channel::Private)
},
Channel::Guild(ref channel) => {
+ let (channel_id, guild_id) = {
+ let channel = channel.read().unwrap();
+
+ (channel.id, channel.guild_id)
+ };
+
+ self.channels.remove(&channel_id);
+
let ch = self.guilds
- .get_mut(&channel.guild_id)
- .map(|guild| guild.channels.remove(&channel.id));
+ .get_mut(&guild_id)
+ .map(|guild| guild.write().unwrap().channels.remove(&channel_id));
match ch {
Some(Some(ch)) => Some(Channel::Guild(ch)),
@@ -527,83 +632,98 @@ impl Cache {
}
#[doc(hidden)]
- pub fn update_with_channel_pins_update(&mut self,
- event: &ChannelPinsUpdateEvent) {
- if let Some(channel) = self.private_channels.get_mut(&event.channel_id) {
- channel.last_pin_timestamp = event.last_pin_timestamp.clone();
+ pub fn update_with_channel_pins_update(&mut self, event: &ChannelPinsUpdateEvent) {
+ if let Some(channel) = self.channels.get(&event.channel_id) {
+ channel.write().unwrap().last_pin_timestamp = event.last_pin_timestamp.clone();
return;
}
- if let Some(group) = self.groups.get_mut(&event.channel_id) {
- group.last_pin_timestamp = event.last_pin_timestamp.clone();
+ if let Some(channel) = self.private_channels.get_mut(&event.channel_id) {
+ channel.write().unwrap().last_pin_timestamp = event.last_pin_timestamp.clone();
return;
}
- // Guild searching is last because it is expensive
- // in comparison to private channel and group searching.
- for guild in self.guilds.values_mut() {
- for channel in guild.channels.values_mut() {
- if channel.id == event.channel_id {
- channel.last_pin_timestamp = event.last_pin_timestamp.clone();
+ if let Some(group) = self.groups.get_mut(&event.channel_id) {
+ group.write().unwrap().last_pin_timestamp = event.last_pin_timestamp.clone();
- return;
- }
- }
+ return;
}
}
#[doc(hidden)]
- pub fn update_with_channel_recipient_add(&mut self,
- event: &ChannelRecipientAddEvent) {
+ pub fn update_with_channel_recipient_add(&mut self, event: &mut ChannelRecipientAddEvent) {
+ self.update_user_entry(&event.user);
+ let user = self.users[&event.user.id].clone();
+
self.groups
.get_mut(&event.channel_id)
- .map(|group| group.recipients.insert(event.user.id,
- event.user.clone()));
+ .map(|group| {
+ group.write()
+ .unwrap()
+ .recipients
+ .insert(event.user.id, user);
+ });
}
#[doc(hidden)]
- pub fn update_with_channel_recipient_remove(&mut self,
- event: &ChannelRecipientRemoveEvent) {
+ pub fn update_with_channel_recipient_remove(&mut self, event: &ChannelRecipientRemoveEvent) {
self.groups
.get_mut(&event.channel_id)
- .map(|group| group.recipients.remove(&event.user.id));
+ .map(|group| group.write().unwrap().recipients.remove(&event.user.id));
}
#[doc(hidden)]
pub fn update_with_channel_update(&mut self, event: &ChannelUpdateEvent) {
match event.channel {
Channel::Group(ref group) => {
- match self.groups.entry(group.channel_id) {
+ let (ch_id, no_recipients) = {
+ let group = group.read().unwrap();
+
+ (group.channel_id, group.recipients.is_empty())
+ };
+
+ match self.groups.entry(ch_id) {
Entry::Vacant(e) => {
e.insert(group.clone());
},
Entry::Occupied(mut e) => {
- let dest = e.get_mut();
+ let mut dest = e.get_mut().write().unwrap();
- if group.recipients.is_empty() {
+ if no_recipients {
let recipients = mem::replace(&mut dest.recipients,
HashMap::new());
- dest.clone_from(group);
+ dest.clone_from(&group.read().unwrap());
dest.recipients = recipients;
} else {
- dest.clone_from(group);
+ dest.clone_from(&group.read().unwrap());
}
},
}
},
Channel::Guild(ref channel) => {
+ let (channel_id, guild_id) = {
+ let channel = channel.read().unwrap();
+
+ (channel.id, channel.guild_id)
+ };
+
+ self.channels.insert(channel_id, channel.clone());
self.guilds
- .get_mut(&channel.guild_id)
- .map(|guild| guild.channels
- .insert(channel.id, channel.clone()));
+ .get_mut(&guild_id)
+ .map(|guild| {
+ guild.write()
+ .unwrap()
+ .channels
+ .insert(channel_id, channel.clone())
+ });
},
Channel::Private(ref channel) => {
self.private_channels
- .get_mut(&channel.id)
+ .get_mut(&channel.read().unwrap().id)
.map(|private| private.clone_from(channel));
},
}
@@ -611,48 +731,67 @@ impl Cache {
#[doc(hidden)]
pub fn update_with_guild_create(&mut self, event: &GuildCreateEvent) {
- self.unavailable_guilds.retain(|guild_id| *guild_id != event.guild.id);
+ self.unavailable_guilds.remove(&event.guild.id);
+
+ let mut guild = event.guild.clone();
- self.guilds.insert(event.guild.id, event.guild.clone());
+ for (user_id, member) in &mut guild.members {
+ self.update_user_entry(&member.user.read().unwrap());
+ let user = self.users[user_id].clone();
+
+ member.user = user.clone();
+ }
+
+ self.channels.extend(guild.channels.clone());
+ self.guilds.insert(event.guild.id, Arc::new(RwLock::new(guild)));
}
#[doc(hidden)]
pub fn update_with_guild_delete(&mut self, event: &GuildDeleteEvent)
- -> Option<Guild> {
- if !self.unavailable_guilds.contains(&event.guild.id) {
- self.unavailable_guilds.push(event.guild.id);
- }
+ -> Option<Arc<RwLock<Guild>>> {
+ // Remove channel entries for the guild if the guild is found.
+ self.guilds.remove(&event.guild.id).map(|guild| {
+ for channel_id in guild.read().unwrap().channels.keys() {
+ self.channels.remove(channel_id);
+ }
- self.guilds.remove(&event.guild.id)
+ guild
+ })
}
#[doc(hidden)]
- pub fn update_with_guild_emojis_update(&mut self,
- event: &GuildEmojisUpdateEvent) {
+ pub fn update_with_guild_emojis_update(&mut self, event: &GuildEmojisUpdateEvent) {
self.guilds
.get_mut(&event.guild_id)
- .map(|guild| guild.emojis.extend(event.emojis.clone()));
+ .map(|guild| guild.write().unwrap().emojis.extend(event.emojis.clone()));
}
#[doc(hidden)]
- pub fn update_with_guild_member_add(&mut self,
- event: &GuildMemberAddEvent) {
+ pub fn update_with_guild_member_add(&mut self, event: &mut GuildMemberAddEvent) {
+ let user_id = event.member.user.read().unwrap().id;
+ self.update_user_entry(&event.member.user.read().unwrap());
+
+ // Always safe due to being inserted above.
+ event.member.user = self.users[&user_id].clone();
+
self.guilds
.get_mut(&event.guild_id)
.map(|guild| {
+ let mut guild = guild.write().unwrap();
+
guild.member_count += 1;
- guild.members.insert(event.member.user.id,
- event.member.clone());
+ guild.members.insert(user_id, event.member.clone());
});
}
#[doc(hidden)]
- pub fn update_with_guild_member_remove(&mut self,
- event: &GuildMemberRemoveEvent)
- -> Option<Member> {
+ pub fn update_with_guild_member_remove(&mut self, event: &GuildMemberRemoveEvent)
+ -> Option<Member> {
let member = self.guilds
.get_mut(&event.guild_id)
.map(|guild| {
+ let mut guild = guild.write().unwrap();
+
guild.member_count -= 1;
guild.members.remove(&event.user.id)
});
@@ -664,10 +803,13 @@ impl Cache {
}
#[doc(hidden)]
- pub fn update_with_guild_member_update(&mut self,
- event: &GuildMemberUpdateEvent)
- -> Option<Member> {
+ pub fn update_with_guild_member_update(&mut self, event: &GuildMemberUpdateEvent)
+ -> Option<Member> {
+ self.update_user_entry(&event.user);
+
if let Some(guild) = self.guilds.get_mut(&event.guild_id) {
+ let mut guild = guild.write().unwrap();
+
let mut found = false;
let item = if let Some(member) = guild.members.get_mut(&event.user.id) {
@@ -675,7 +817,7 @@ impl Cache {
member.nick.clone_from(&event.nick);
member.roles.clone_from(&event.roles);
- member.user.clone_from(&event.user);
+ member.user.write().unwrap().clone_from(&event.user);
found = true;
@@ -691,7 +833,7 @@ impl Cache {
mute: false,
nick: event.nick.clone(),
roles: event.roles.clone(),
- user: event.user.clone(),
+ user: Arc::new(RwLock::new(event.user.clone())),
});
}
@@ -702,28 +844,34 @@ impl Cache {
}
#[doc(hidden)]
- pub fn update_with_guild_members_chunk(&mut self,
- event: &GuildMembersChunkEvent) {
+ pub fn update_with_guild_members_chunk(&mut self, event: &GuildMembersChunkEvent) {
+ for member in event.members.values() {
+ self.update_user_entry(&member.user.read().unwrap());
+ }
+
self.guilds
.get_mut(&event.guild_id)
- .map(|guild| guild.members.extend(event.members.clone()));
+ .map(|guild| {
+ guild.write().unwrap().members.extend(event.members.clone())
+ });
}
#[doc(hidden)]
- pub fn update_with_guild_role_create(&mut self,
- event: &GuildRoleCreateEvent) {
+ pub fn update_with_guild_role_create(&mut self, event: &GuildRoleCreateEvent) {
self.guilds
.get_mut(&event.guild_id)
- .map(|guild| guild.roles.insert(event.role.id, event.role.clone()));
+ .map(|guild| {
+ guild.write().unwrap().roles.insert(event.role.id, event.role.clone())
+ });
}
#[doc(hidden)]
- pub fn update_with_guild_role_delete(&mut self,
- event: &GuildRoleDeleteEvent)
- -> Option<Role> {
+ pub fn update_with_guild_role_delete(&mut self, event: &GuildRoleDeleteEvent) -> Option<Role> {
let role = self.guilds
.get_mut(&event.guild_id)
- .map(|guild| guild.roles.remove(&event.role_id));
+ .map(|guild| {
+ guild.write().unwrap().roles.remove(&event.role_id)
+ });
match role {
Some(Some(x)) => Some(x),
@@ -732,14 +880,16 @@ impl Cache {
}
#[doc(hidden)]
- pub fn update_with_guild_role_update(&mut self,
- event: &GuildRoleUpdateEvent)
- -> Option<Role> {
+ pub fn update_with_guild_role_update(&mut self, event: &GuildRoleUpdateEvent) -> Option<Role> {
let item = self.guilds
.get_mut(&event.guild_id)
- .map(|guild| guild.roles
- .get_mut(&event.role.id)
- .map(|role| mem::replace(role, event.role.clone())));
+ .map(|guild| {
+ guild.write()
+ .unwrap()
+ .roles
+ .get_mut(&event.role.id)
+ .map(|role| mem::replace(role, event.role.clone()))
+ });
match item {
Some(Some(x)) => Some(x),
@@ -749,9 +899,15 @@ impl Cache {
#[doc(hidden)]
pub fn update_with_guild_sync(&mut self, event: &GuildSyncEvent) {
+ for member in event.members.values() {
+ self.update_user_entry(&member.user.read().unwrap());
+ }
+
self.guilds
.get_mut(&event.guild_id)
.map(|guild| {
+ let mut guild = guild.write().unwrap();
+
guild.large = event.large;
guild.members.clone_from(&event.members);
guild.presences.clone_from(&event.presences);
@@ -759,11 +915,9 @@ impl Cache {
}
#[doc(hidden)]
- pub fn update_with_guild_unavailable(&mut self,
- event: &GuildUnavailableEvent) {
- if !self.unavailable_guilds.contains(&event.guild_id) {
- self.unavailable_guilds.push(event.guild_id);
- }
+ pub fn update_with_guild_unavailable(&mut self, event: &GuildUnavailableEvent) {
+ self.unavailable_guilds.insert(event.guild_id);
+ self.guilds.remove(&event.guild_id);
}
#[doc(hidden)]
@@ -771,6 +925,8 @@ impl Cache {
self.guilds
.get_mut(&event.guild.id)
.map(|guild| {
+ let mut guild = guild.write().unwrap();
+
guild.afk_timeout = event.guild.afk_timeout;
guild.afk_channel_id.clone_from(&event.guild.afk_channel_id);
guild.icon.clone_from(&event.guild.icon);
@@ -784,8 +940,8 @@ impl Cache {
#[doc(hidden)]
pub fn update_with_presences_replace(&mut self, event: &PresencesReplaceEvent) {
- self.presences.clone_from(&{
- let mut p = HashMap::default();
+ self.presences.extend({
+ let mut p: HashMap<UserId, Presence> = HashMap::default();
for presence in &event.presences {
p.insert(presence.user_id, presence.clone());
@@ -796,41 +952,51 @@ impl Cache {
}
#[doc(hidden)]
- pub fn update_with_presence_update(&mut self, event: &PresenceUpdateEvent) {
+ pub fn update_with_presence_update(&mut self, event: &mut PresenceUpdateEvent) {
+ let user_id = event.presence.user_id;
+
+ if let Some(user) = event.presence.user.as_mut() {
+ self.update_user_entry(&user.read().unwrap());
+ *user = self.users[&user_id].clone();
+ }
+
if let Some(guild_id) = event.guild_id {
if let Some(guild) = self.guilds.get_mut(&guild_id) {
- // If the user was modified, update the member list
- if let Some(user) = event.presence.user.as_ref() {
- guild.members
- .get_mut(&user.id)
- .map(|member| member.user.clone_from(user));
- }
+ let mut guild = guild.write().unwrap();
- update_presence(&mut guild.presences, &event.presence);
+ // If the member went offline, remove them from the presence list.
+ if event.presence.status == OnlineStatus::Offline {
+ guild.presences.remove(&event.presence.user_id);
+ } else {
+ guild.presences.insert(event.presence.user_id, event.presence.clone());
+ }
}
+ } else if event.presence.status != OnlineStatus::Offline {
+ self.presences.insert(event.presence.user_id, event.presence.clone());
+ } else {
+ self.presences.remove(&event.presence.user_id);
}
}
#[doc(hidden)]
- pub fn update_with_ready(&mut self, ready: &ReadyEvent) {
- let ready = ready.ready.clone();
+ pub fn update_with_ready(&mut self, event: &ReadyEvent) {
+ let mut ready = event.ready.clone();
for guild in ready.guilds {
match guild {
PossibleGuild::Offline(guild_id) => {
- self.unavailable_guilds.push(guild_id);
- }
+ self.unavailable_guilds.insert(guild_id);
+ self.guilds.remove(&guild_id);
+ },
PossibleGuild::Online(guild) => {
- self.guilds.insert(guild.id, guild);
+ self.channels.extend(guild.channels.clone());
+ self.guilds.insert(guild.id, Arc::new(RwLock::new(guild)));
},
}
}
- self.unavailable_guilds.sort();
- self.unavailable_guilds.dedup();
-
// The private channels sent in the READY contains both the actual
- // private channels, and the groups.
+ // private channels and the groups.
for (channel_id, channel) in ready.private_channels {
match channel {
Channel::Group(group) => {
@@ -839,53 +1005,53 @@ impl Cache {
Channel::Private(channel) => {
self.private_channels.insert(channel_id, channel);
},
- Channel::Guild(_) => {},
+ Channel::Guild(guild) => warn!("Got a guild in DMs: {:?}", guild),
}
}
- for guild in ready.user_guild_settings.unwrap_or_default() {
- self.guild_settings.insert(guild.guild_id, guild);
+ if let Some(user_guild_settings) = ready.user_guild_settings {
+ for guild in user_guild_settings {
+ self.guild_settings.insert(guild.guild_id, guild);
+ }
}
- for (user_id, presence) in ready.presences {
- self.presences.insert(user_id, presence);
- }
+ for (user_id, presence) in &mut ready.presences {
+ if let Some(ref user) = presence.user {
+ self.update_user_entry(&user.read().unwrap());
+ }
- for (user_id, relationship) in ready.relationships {
- self.relationships.insert(user_id, relationship);
+ presence.user = self.users.get(user_id).cloned();
}
+ self.presences.extend(ready.presences);
+ self.relationships.extend(ready.relationships);
self.notes.extend(ready.notes);
-
self.settings = ready.user_settings;
self.user = ready.user;
}
#[doc(hidden)]
pub fn update_with_relationship_add(&mut self, event: &RelationshipAddEvent) {
- self.relationships.insert(event.relationship.id,
- event.relationship.clone());
+ self.update_user_entry(&event.relationship.user);
+
+ self.relationships.insert(event.relationship.id, event.relationship.clone());
}
#[doc(hidden)]
- pub fn update_with_relationship_remove(&mut self,
- event: &RelationshipRemoveEvent) {
+ pub fn update_with_relationship_remove(&mut self, event: &RelationshipRemoveEvent) {
self.relationships.remove(&event.user_id);
}
#[doc(hidden)]
- pub fn update_with_user_guild_settings_update(&mut self,
- event: &UserGuildSettingsUpdateEvent)
- -> Option<UserGuildSettings> {
+ pub fn update_with_user_guild_settings_update(&mut self, event: &UserGuildSettingsUpdateEvent)
+ -> Option<UserGuildSettings> {
self.guild_settings
.get_mut(&event.settings.guild_id)
.map(|guild_setting| mem::replace(guild_setting, event.settings.clone()))
}
#[doc(hidden)]
- pub fn update_with_user_note_update(&mut self,
- event: &UserNoteUpdateEvent)
- -> Option<String> {
+ pub fn update_with_user_note_update(&mut self, event: &UserNoteUpdateEvent) -> Option<String> {
if event.note.is_empty() {
self.notes.remove(&event.user_id)
} else {
@@ -894,10 +1060,8 @@ impl Cache {
}
#[doc(hidden)]
- pub fn update_with_user_settings_update(&mut self,
- event: &UserSettingsUpdateEvent,
- old: bool)
- -> Option<UserSettings> {
+ pub fn update_with_user_settings_update(&mut self, event: &UserSettingsUpdateEvent, old: bool)
+ -> Option<UserSettings> {
let item = if old {
self.settings.clone()
} else {
@@ -923,21 +1087,20 @@ impl Cache {
}
#[doc(hidden)]
- pub fn update_with_user_update(&mut self, event: &UserUpdateEvent)
- -> CurrentUser {
+ pub fn update_with_user_update(&mut self, event: &UserUpdateEvent) -> CurrentUser {
mem::replace(&mut self.user, event.current_user.clone())
}
#[doc(hidden)]
- pub fn update_with_voice_state_update(&mut self,
- event: &VoiceStateUpdateEvent) {
+ pub fn update_with_voice_state_update(&mut self, event: &VoiceStateUpdateEvent) {
if let Some(guild_id) = event.guild_id {
if let Some(guild) = self.guilds.get_mut(&guild_id) {
+ let mut guild = guild.write().unwrap();
+
if event.voice_state.channel_id.is_some() {
// Update or add to the voice state list
{
- let finding = guild.voice_states
- .get_mut(&event.voice_state.user_id);
+ let finding = guild.voice_states.get_mut(&event.voice_state.user_id);
if let Some(srv_state) = finding {
srv_state.clone_from(&event.voice_state);
@@ -960,6 +1123,8 @@ impl Cache {
if let Some(channel) = event.voice_state.channel_id {
// channel id available, insert voice state
if let Some(call) = self.calls.get_mut(&channel) {
+ let mut call = call.write().unwrap();
+
{
let finding = call.voice_states
.get_mut(&event.voice_state.user_id);
@@ -971,13 +1136,26 @@ impl Cache {
}
}
- call.voice_states.insert(event.voice_state.user_id,
- event.voice_state.clone());
+ call.voice_states.insert(event.voice_state.user_id, event.voice_state.clone());
}
} else {
// delete this user from any group call containing them
for call in self.calls.values_mut() {
- call.voice_states.remove(&event.voice_state.user_id);
+ call.write().unwrap().voice_states.remove(&event.voice_state.user_id);
+ }
+ }
+ }
+
+ // Adds or updates a user entry in the [`users`] map with a received user.
+ //
+ // [`users`]: #structfield.users
+ fn update_user_entry(&mut self, user: &User) {
+ match self.users.entry(user.id) {
+ Entry::Vacant(e) => {
+ e.insert(Arc::new(RwLock::new(user.clone())));
+ },
+ Entry::Occupied(mut e) => {
+ e.get_mut().write().unwrap().clone_from(user);
}
}
}
@@ -987,6 +1165,7 @@ impl Default for Cache {
fn default() -> Cache {
Cache {
calls: HashMap::default(),
+ channels: HashMap::default(),
groups: HashMap::default(),
guild_settings: HashMap::default(),
guilds: HashMap::default(),
@@ -995,7 +1174,7 @@ impl Default for Cache {
private_channels: HashMap::default(),
relationships: HashMap::default(),
settings: None,
- unavailable_guilds: Vec::default(),
+ unavailable_guilds: HashSet::default(),
user: CurrentUser {
avatar: None,
bot: false,
@@ -1006,46 +1185,8 @@ impl Default for Cache {
mobile: None,
name: String::default(),
verified: false,
- }
- }
- }
-}
-
-fn update_presence(presences: &mut HashMap<UserId, Presence>,
- presence: &Presence) {
- if presence.status == OnlineStatus::Offline {
- // Remove the user from the presence list
- presences.remove(&presence.user_id);
- } else {
- // Update or add to the presence list
- if let Some(ref mut guild_presence) = presences.get(&presence.user_id) {
- if presence.user.is_none() {
- guild_presence.clone_from(&presence);
- }
-
- return;
- }
- presences.insert(presence.user_id, presence.clone());
- }
-}
-
-/// A reference to a private channel, guild's channel, or group.
-pub enum ChannelRef<'a> {
- /// A group's channel
- Group(&'a Group),
- /// A guild channel and its guild
- Guild(&'a GuildChannel),
- /// A private channel
- Private(&'a PrivateChannel),
-}
-
-impl<'a> ChannelRef<'a> {
- /// Clones the inner value of the variant.
- pub fn clone_inner(&self) -> Channel {
- match *self {
- ChannelRef::Group(group) => Channel::Group(group.clone()),
- ChannelRef::Guild(channel) => Channel::Guild(channel.clone()),
- ChannelRef::Private(private) => Channel::Private(private.clone()),
+ },
+ users: HashMap::default(),
}
}
}
diff --git a/src/ext/framework/mod.rs b/src/ext/framework/mod.rs
index 76f3418..30a5c8f 100644
--- a/src/ext/framework/mod.rs
+++ b/src/ext/framework/mod.rs
@@ -74,13 +74,11 @@ use std::default::Default;
use std::sync::Arc;
use std::thread;
use ::client::Context;
-use ::model::{Message, UserId};
+use ::model::{Channel, Message, UserId};
use ::utils;
#[cfg(feature="cache")]
use ::client::CACHE;
-#[cfg(feature="cache")]
-use ::ext::cache::ChannelRef;
/// A macro to generate "named parameters". This is useful to avoid manually
/// using the "arguments" parameter and manually parsing types.
@@ -410,7 +408,7 @@ impl Framework {
let guild_id = {
match CACHE.read().unwrap().get_channel(message.channel_id) {
- Some(ChannelRef::Guild(channel)) => Some(channel.guild_id),
+ Some(Channel::Guild(channel)) => Some(channel.read().unwrap().guild_id),
_ => None,
}
};
@@ -425,7 +423,7 @@ impl Framework {
}
if let Some(guild) = guild_id.find() {
- if self.configuration.blocked_users.contains(&guild.owner_id) {
+ if self.configuration.blocked_users.contains(&guild.read().unwrap().owner_id) {
if let Some(ref message) = self.configuration.blocked_guild_message {
let _ = context.say(message);
}
@@ -515,9 +513,11 @@ impl Framework {
let member = {
let mut member_found = None;
- if let Some(ChannelRef::Guild(channel)) = cache.get_channel(message.channel_id) {
- if let Some(guild) = channel.guild_id.find() {
- if let Some(member) = guild.members.get(&message.author.id) {
+ if let Some(Channel::Guild(channel)) = cache.get_channel(message.channel_id) {
+ let guild_id = channel.read().unwrap().guild_id;
+
+ if let Some(guild) = guild_id.find() {
+ if let Some(member) = guild.read().unwrap().members.get(&message.author.id) {
member_found = Some(member.clone());
}
}
@@ -529,7 +529,7 @@ impl Framework {
if let Some(member) = member {
if let Ok(guild_id) = member.find_guild() {
if let Some(guild) = cache.get_guild(guild_id) {
- let perms = guild.permissions_for(message.channel_id, message.author.id);
+ let perms = guild.read().unwrap().permissions_for(message.channel_id, message.author.id);
permissions_fulfilled = perms.contains(command.required_permissions);
}
diff --git a/src/model/channel.rs b/src/model/channel.rs
index ba5dcc2..e6ebb27 100644
--- a/src/model/channel.rs
+++ b/src/model/channel.rs
@@ -4,6 +4,7 @@ use std::borrow::Cow;
use std::fmt::{self, Write};
use std::io::Read;
use std::mem;
+use std::sync::{Arc, RwLock};
use super::utils::{
decode_id,
into_map,
@@ -29,8 +30,6 @@ use ::utils::decode_array;
use super::utils;
#[cfg(feature="cache")]
use ::client::CACHE;
-#[cfg(feature="cache")]
-use ::ext::cache::ChannelRef;
impl Attachment {
/// If this attachment is an image, then a tuple of the width and height
@@ -170,11 +169,11 @@ impl Channel {
let map = into_map(value)?;
match req!(map.get("type").and_then(|x| x.as_u64())) {
0 | 2 => GuildChannel::decode(Value::Object(map))
- .map(Channel::Guild),
+ .map(|x| Channel::Guild(Arc::new(RwLock::new(x)))),
1 => PrivateChannel::decode(Value::Object(map))
- .map(Channel::Private),
+ .map(|x| Channel::Private(Arc::new(RwLock::new(x)))),
3 => Group::decode(Value::Object(map))
- .map(Channel::Group),
+ .map(|x| Channel::Group(Arc::new(RwLock::new(x)))),
other => Err(Error::Decode("Expected value Channel type",
Value::U64(other))),
}
@@ -189,13 +188,13 @@ impl Channel {
pub fn delete(&self) -> Result<()> {
match *self {
Channel::Group(ref group) => {
- let _ = group.leave()?;
+ let _ = group.read().unwrap().leave()?;
},
Channel::Guild(ref public_channel) => {
- let _ = public_channel.delete()?;
+ let _ = public_channel.read().unwrap().delete()?;
},
Channel::Private(ref private_channel) => {
- let _ = private_channel.delete()?;
+ let _ = private_channel.read().unwrap().delete()?;
},
}
@@ -325,9 +324,9 @@ impl Channel {
/// [`PrivateChannel`]: struct.PrivateChannel.html
pub fn id(&self) -> ChannelId {
match *self {
- Channel::Group(ref group) => group.channel_id,
- Channel::Guild(ref channel) => channel.id,
- Channel::Private(ref channel) => channel.id,
+ Channel::Group(ref group) => group.read().unwrap().channel_id,
+ Channel::Guild(ref channel) => channel.read().unwrap().id,
+ Channel::Private(ref channel) => channel.read().unwrap().id,
}
}
@@ -386,13 +385,20 @@ impl fmt::Display for Channel {
/// [`GuildChannel`]: struct.GuildChannel.html
/// [`PrivateChannel`]: struct.PrivateChannel.html
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let out = match *self {
- Channel::Group(ref group) => group.name().to_owned(),
- Channel::Guild(ref channel) => Cow::Owned(format!("{}", channel)),
- Channel::Private(ref channel) => Cow::Owned(channel.recipient.name.clone()),
- };
+ match *self {
+ Channel::Group(ref group) => {
+ fmt::Display::fmt(&group.read().unwrap().name(), f)
+ },
+ Channel::Guild(ref ch) => {
+ fmt::Display::fmt(&ch.read().unwrap().id.mention(), f)
+ },
+ Channel::Private(ref ch) => {
+ let channel = ch.read().unwrap();
+ let recipient = channel.recipient.read().unwrap();
- fmt::Display::fmt(&out, f)
+ fmt::Display::fmt(&recipient.name, f)
+ },
+ }
}
}
@@ -615,7 +621,7 @@ impl ChannelId {
/// Search the cache for the channel with the Id.
#[cfg(feature="cache")]
pub fn find(&self) -> Option<Channel> {
- CACHE.read().unwrap().get_channel(*self).map(|x| x.clone_inner())
+ CACHE.read().unwrap().get_channel(*self)
}
/// Search the cache for the channel. If it can't be found, the channel is
@@ -624,7 +630,7 @@ impl ChannelId {
#[cfg(feature="cache")]
{
if let Some(channel) = CACHE.read().unwrap().get_channel(*self) {
- return Ok(channel.clone_inner());
+ return Ok(channel);
}
}
@@ -837,9 +843,9 @@ impl From<Channel> for ChannelId {
/// Gets the Id of a `Channel`.
fn from(channel: Channel) -> ChannelId {
match channel {
- Channel::Group(group) => group.channel_id,
- Channel::Guild(channel) => channel.id,
- Channel::Private(channel) => channel.id,
+ Channel::Group(group) => group.read().unwrap().channel_id,
+ Channel::Guild(ch) => ch.read().unwrap().id,
+ Channel::Private(ch) => ch.read().unwrap().id,
}
}
}
@@ -1054,12 +1060,12 @@ impl Group {
Some(ref name) => Cow::Borrowed(name),
None => {
let mut name = match self.recipients.values().nth(0) {
- Some(recipient) => recipient.name.clone(),
+ Some(recipient) => recipient.read().unwrap().name.clone(),
None => return Cow::Borrowed("Empty Group"),
};
for recipient in self.recipients.values().skip(1) {
- let _ = write!(name, ", {}", recipient.name);
+ let _ = write!(name, ", {}", recipient.read().unwrap().name);
}
Cow::Owned(name)
@@ -1327,7 +1333,7 @@ impl Message {
#[cfg(feature="cache")]
pub fn guild_id(&self) -> Option<GuildId> {
match CACHE.read().unwrap().get_channel(self.channel_id) {
- Some(ChannelRef::Guild(channel)) => Some(channel.guild_id),
+ Some(Channel::Guild(ch)) => Some(ch.read().unwrap().guild_id),
_ => None,
}
}
@@ -1336,7 +1342,7 @@ impl Message {
#[cfg(feature="cache")]
pub fn is_private(&self) -> bool {
match CACHE.read().unwrap().get_channel(self.channel_id) {
- Some(ChannelRef::Group(_)) | Some(ChannelRef::Private(_)) => true,
+ Some(Channel::Group(_)) | Some(Channel::Private(_)) => true,
_ => false,
}
}
@@ -1568,14 +1574,14 @@ impl PrivateChannel {
pub fn decode(value: Value) -> Result<PrivateChannel> {
let mut map = into_map(value)?;
let mut recipients = decode_array(remove(&mut map, "recipients")?,
- User::decode)?;
+ User::decode)?;
Ok(PrivateChannel {
id: remove(&mut map, "id").and_then(ChannelId::decode)?,
kind: remove(&mut map, "type").and_then(ChannelType::decode)?,
last_message_id: opt(&mut map, "last_message_id", MessageId::decode)?,
last_pin_timestamp: opt(&mut map, "last_pin_timestamp", into_string)?,
- recipient: recipients.remove(0),
+ recipient: Arc::new(RwLock::new(recipients.remove(0))),
})
}
@@ -1750,7 +1756,7 @@ impl PrivateChannel {
impl fmt::Display for PrivateChannel {
/// Formats the private channel, displaying the recipient's username.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str(&self.recipient.name)
+ f.write_str(&self.recipient.read().unwrap().name)
}
}
@@ -2093,8 +2099,8 @@ 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<Guild> {
- CACHE.read().unwrap().get_guild(self.guild_id).cloned()
+ pub fn guild(&self) -> Option<Arc<RwLock<Guild>>> {
+ CACHE.read().unwrap().get_guild(self.guild_id)
}
/// Pins a [`Message`] to the channel.
diff --git a/src/model/gateway.rs b/src/model/gateway.rs
index 9758e4b..dc25d0d 100644
--- a/src/model/gateway.rs
+++ b/src/model/gateway.rs
@@ -1,3 +1,4 @@
+use std::sync::{Arc, RwLock};
use super::utils::*;
use super::*;
use ::internal::prelude::*;
@@ -69,7 +70,7 @@ impl Presence {
status: remove(&mut value, "status").and_then(OnlineStatus::decode_str)?,
last_modified: opt(&mut value, "last_modified", |v| Ok(req!(v.as_u64())))?,
game: game,
- user: user,
+ user: user.map(RwLock::new).map(Arc::new),
nick: opt(&mut value, "nick", into_string)?,
})
}
diff --git a/src/model/guild.rs b/src/model/guild.rs
index 0d557c6..0ee68fd 100644
--- a/src/model/guild.rs
+++ b/src/model/guild.rs
@@ -1,6 +1,8 @@
use serde_json::builder::ObjectBuilder;
+use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::HashMap;
+use std::sync::{Arc, RwLock};
use std::fmt;
use super::utils::{
decode_emojis,
@@ -95,12 +97,15 @@ impl Emoji {
/// [`Guild`]: struct.Guild.html
#[cfg(feature="cache")]
pub fn find_guild_id(&self) -> Option<GuildId> {
- CACHE.read()
- .unwrap()
- .guilds
- .values()
- .find(|guild| guild.emojis.contains_key(&self.id))
- .map(|guild| guild.id)
+ for guild in CACHE.read().unwrap().guilds.values() {
+ let guild = guild.read().unwrap();
+
+ if guild.emojis.contains_key(&self.id) {
+ return Some(guild.id);
+ }
+ }
+
+ None
}
/// Generates a URL to the emoji's image.
@@ -161,7 +166,7 @@ impl Guild {
None => return Err(Error::Client(ClientError::ItemMissing)),
};
- let perms = self.permissions_for(ChannelId(self.id.0), member.user.id);
+ let perms = self.permissions_for(ChannelId(self.id.0), member.user.read().unwrap().id);
permissions.remove(perms);
Ok(permissions.is_empty())
@@ -387,23 +392,23 @@ impl Guild {
let id = remove(&mut map, "id").and_then(GuildId::decode)?;
- let public_channels = {
- let mut public_channels = HashMap::new();
+ let channels = {
+ let mut channels = HashMap::new();
let vals = decode_array(remove(&mut map, "channels")?,
|v| GuildChannel::decode_guild(v, id))?;
- for public_channel in vals {
- public_channels.insert(public_channel.id, public_channel);
+ for channel in vals {
+ channels.insert(channel.id, Arc::new(RwLock::new(channel)));
}
- public_channels
+ channels
};
Ok(Guild {
afk_channel_id: opt(&mut map, "afk_channel_id", ChannelId::decode)?,
afk_timeout: req!(remove(&mut map, "afk_timeout")?.as_u64()),
- channels: public_channels,
+ channels: channels,
default_message_notifications: req!(remove(&mut map, "default_message_notifications")?.as_u64()),
emojis: remove(&mut map, "emojis").and_then(decode_emojis)?,
features: remove(&mut map, "features").and_then(|v| decode_array(v, Feature::decode_str))?,
@@ -756,9 +761,9 @@ impl Guild {
self.members
.values()
.find(|member| {
- let name_matches = member.user.name == name;
+ let name_matches = member.user.read().unwrap().name == name;
let discrim_matches = match discrim {
- Some(discrim) => member.user.discriminator == discrim,
+ Some(discrim) => member.user.read().unwrap().discriminator == discrim,
None => true,
};
@@ -889,7 +894,7 @@ impl Guild {
permissions |= role.permissions;
} else {
warn!("(╯°□°)╯︵ ┻━┻ {} on {} has non-existent role {:?}",
- member.user.id,
+ member.user.read().unwrap().id,
self.id,
role);
}
@@ -901,6 +906,8 @@ impl Guild {
}
if let Some(channel) = self.channels.get(&channel_id) {
+ let channel = channel.read().unwrap();
+
// If this is a text channel, then throw out voice permissions.
if channel.kind == ChannelType::Text {
permissions &= !(CONNECT | SPEAK | MUTE_MEMBERS |
@@ -1366,8 +1373,8 @@ impl GuildId {
/// Search the cache for the guild.
#[cfg(feature="cache")]
- pub fn find(&self) -> Option<Guild> {
- CACHE.read().unwrap().get_guild(*self).cloned()
+ pub fn find(&self) -> Option<Arc<RwLock<Guild>>> {
+ CACHE.read().unwrap().get_guild(*self)
}
/// Requests the guild over REST.
@@ -1650,7 +1657,7 @@ impl Member {
let guild_id = self.find_guild()?;
- match rest::add_member_role(guild_id.0, self.user.id.0, role_id.0) {
+ match rest::add_member_role(guild_id.0, self.user.read().unwrap().id.0, role_id.0) {
Ok(()) => {
self.roles.push(role_id);
@@ -1674,7 +1681,7 @@ impl Member {
let map = EditMember::default().roles(&self.roles).0.build();
- match rest::edit_member(guild_id.0, self.user.id.0, map) {
+ match rest::edit_member(guild_id.0, self.user.read().unwrap().id.0, map) {
Ok(()) => Ok(()),
Err(why) => {
self.roles.retain(|r| !role_ids.contains(r));
@@ -1699,7 +1706,7 @@ impl Member {
/// [Ban Members]: permissions/constant.BAN_MEMBERS.html
#[cfg(feature="cache")]
pub fn ban(&self, delete_message_days: u8) -> Result<()> {
- rest::ban_user(self.find_guild()?.0, self.user.id.0, delete_message_days)
+ rest::ban_user(self.find_guild()?.0, self.user.read().unwrap().id.0, delete_message_days)
}
/// Determines the member's colour.
@@ -1712,13 +1719,13 @@ impl Member {
let cache = CACHE.read().unwrap();
let guild = match cache.guilds.get(&guild_id) {
- Some(guild) => guild,
+ Some(guild) => guild.read().unwrap(),
None => return None,
};
let mut roles = self.roles
.iter()
- .filter_map(|id| guild.roles.get(id))
+ .filter_map(|role_id| guild.roles.get(role_id))
.collect::<Vec<&Role>>();
roles.sort_by(|a, b| b.cmp(a));
@@ -1731,14 +1738,16 @@ impl Member {
///
/// The nickname takes priority over the member's username if it exists.
#[inline]
- pub fn display_name(&self) -> &str {
- self.nick.as_ref().unwrap_or(&self.user.name)
+ pub fn display_name(&self) -> Cow<String> {
+ self.nick.as_ref()
+ .map(Cow::Borrowed)
+ .unwrap_or_else(|| Cow::Owned(self.user.read().unwrap().name.clone()))
}
/// Returns the DiscordTag of a Member, taking possible nickname into account.
#[inline]
pub fn distinct(&self) -> String {
- format!("{}#{}", self.display_name(), self.user.discriminator)
+ format!("{}#{}", self.display_name(), self.user.read().unwrap().discriminator)
}
/// Edits the member with the given data. See [`Context::edit_member`] for
@@ -1754,7 +1763,7 @@ impl Member {
let guild_id = self.find_guild()?;
let map = f(EditMember::default()).0.build();
- rest::edit_member(guild_id.0, self.user.id.0, map)
+ rest::edit_member(guild_id.0, self.user.read().unwrap().id.0, map)
}
/// Finds the Id of the [`Guild`] that the member is in.
@@ -1768,22 +1777,19 @@ impl Member {
/// [`Guild`]: struct.Guild.html
#[cfg(feature="cache")]
pub fn find_guild(&self) -> Result<GuildId> {
- CACHE.read()
- .unwrap()
- .guilds
- .values()
- .find(|guild| {
- guild.members
- .values()
- .any(|member| {
- let joined_at = member.joined_at == self.joined_at;
- let user_id = member.user.id == self.user.id;
+ for guild in CACHE.read().unwrap().guilds.values() {
+ let guild = guild.read().unwrap();
+
+ let predicate = guild.members
+ .values()
+ .any(|m| m.joined_at == self.joined_at && m.user.read().unwrap().id == self.user.read().unwrap().id);
- joined_at && user_id
- })
- })
- .map(|x| x.id)
- .ok_or(Error::Client(ClientError::GuildNotFound))
+ if predicate {
+ return Ok(guild.id);
+ }
+ }
+
+ Err(Error::Client(ClientError::GuildNotFound))
}
/// Removes a [`Role`] from the member, editing its roles in-place if the
@@ -1803,7 +1809,7 @@ impl Member {
let guild_id = self.find_guild()?;
- match rest::remove_member_role(guild_id.0, self.user.id.0, role_id.0) {
+ match rest::remove_member_role(guild_id.0, self.user.read().unwrap().id.0, role_id.0) {
Ok(()) => {
self.roles.retain(|r| r.0 != role_id.0);
@@ -1826,7 +1832,7 @@ impl Member {
let map = EditMember::default().roles(&self.roles).0.build();
- match rest::edit_member(guild_id.0, self.user.id.0, map) {
+ match rest::edit_member(guild_id.0, self.user.read().unwrap().id.0, map) {
Ok(()) => Ok(()),
Err(why) => {
self.roles.extend_from_slice(role_ids);
@@ -1846,12 +1852,18 @@ impl Member {
CACHE.read().unwrap()
.guilds
.values()
- .find(|g| g.members
+ .find(|guild| guild
+ .read()
+ .unwrap()
+ .members
.values()
- .any(|m| m.user.id == self.user.id && m.joined_at == *self.joined_at))
- .map(|g| g.roles
+ .any(|m| m.user.read().unwrap().id == self.user.read().unwrap().id && m.joined_at == *self.joined_at))
+ .map(|guild| guild
+ .read()
+ .unwrap()
+ .roles
.values()
- .filter(|r| self.roles.contains(&r.id))
+ .filter(|role| self.roles.contains(&role.id))
.cloned()
.collect())
}
@@ -1870,7 +1882,7 @@ impl Member {
/// [Ban Members]: permissions/constant.BAN_MEMBERS.html
#[cfg(feature="cache")]
pub fn unban(&self) -> Result<()> {
- rest::remove_ban(self.find_guild()?.0, self.user.id.0)
+ rest::remove_ban(self.find_guild()?.0, self.user.read().unwrap().id.0)
}
}
@@ -1886,7 +1898,7 @@ impl fmt::Display for Member {
///
// This is in the format of `<@USER_ID>`.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.user.mention(), f)
+ fmt::Display::fmt(&self.user.read().unwrap().mention(), f)
}
}
@@ -2457,13 +2469,15 @@ impl Role {
/// [`ClientError::GuildNotFound`]: ../client/enum.ClientError.html#variant.GuildNotFound
#[cfg(feature="cache")]
pub fn find_guild(&self) -> Result<GuildId> {
- CACHE.read()
- .unwrap()
- .guilds
- .values()
- .find(|guild| guild.roles.contains_key(&RoleId(self.id.0)))
- .map(|x| x.id)
- .ok_or(Error::Client(ClientError::GuildNotFound))
+ for guild in CACHE.read().unwrap().guilds.values() {
+ let guild = guild.read().unwrap();
+
+ if guild.roles.contains_key(&RoleId(self.id.0)) {
+ return Ok(guild.id);
+ }
+ }
+
+ Err(Error::Client(ClientError::GuildNotFound))
}
/// Check that the role has the given permission.
@@ -2523,17 +2537,21 @@ impl RoleId {
/// Search the cache for the role.
#[cfg(feature="cache")]
pub fn find(&self) -> Option<Role> {
- CACHE.read()
- .unwrap()
- .guilds
- .values()
- .find(|guild| guild.roles.contains_key(self))
- .map(|guild| guild.roles.get(self))
- .and_then(|v| match v {
- Some(v) => Some(v),
- None => None,
- })
- .cloned()
+ let cache = CACHE.read().unwrap();
+
+ for guild in cache.guilds.values() {
+ let guild = guild.read().unwrap();
+
+ if !guild.roles.contains_key(self) {
+ continue;
+ }
+
+ if let Some(role) = guild.roles.get(self) {
+ return Some(role.clone());
+ }
+ }
+
+ None
}
}
diff --git a/src/model/misc.rs b/src/model/misc.rs
index eaae890..3784a59 100644
--- a/src/model/misc.rs
+++ b/src/model/misc.rs
@@ -30,13 +30,13 @@ impl Mentionable for Channel {
fn mention(&self) -> String {
match *self {
Channel::Guild(ref x) => {
- format!("<#{}>", x.id.0)
+ format!("<#{}>", x.read().unwrap().id.0)
},
Channel::Private(ref x) => {
- format!("<#{}>", x.id.0)
+ format!("<#{}>", x.read().unwrap().id.0)
},
Channel::Group(ref x) => {
- format!("<#{}>", x.channel_id.0)
+ format!("<#{}>", x.read().unwrap().channel_id.0)
}
}
}
@@ -50,7 +50,7 @@ impl Mentionable for Emoji {
impl Mentionable for Member {
fn mention(&self) -> String {
- format!("<@{}>", self.user.id.0)
+ format!("<@{}>", self.user.read().unwrap().id.0)
}
}
@@ -86,7 +86,7 @@ impl FromStr for User {
match utils::parse_username(s) {
Some(x) => {
match UserId(x as u64).find() {
- Some(user) => Ok(user),
+ Some(user) => Ok(user.read().unwrap().clone()),
_ => Err(())
}
},
diff --git a/src/model/mod.rs b/src/model/mod.rs
index 2a4f97d..790e378 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -39,6 +39,7 @@ pub use self::webhook::*;
use self::utils::*;
use std::collections::HashMap;
+use std::sync::{Arc, RwLock};
use time::Timespec;
use ::internal::prelude::*;
use ::utils::{Colour, decode_array};
@@ -123,18 +124,18 @@ id! {
#[derive(Clone, Debug)]
pub enum Channel {
/// A group. A group comprises of only one channel.
- Group(Group),
+ Group(Arc<RwLock<Group>>),
/// A [text] or [voice] channel within a [`Guild`].
///
/// [`Guild`]: struct.Guild.html
/// [text]: enum.ChannelType.html#variant.Text
/// [voice]: enum.ChannelType.html#variant.Voice
- Guild(GuildChannel),
+ Guild(Arc<RwLock<GuildChannel>>),
/// A private channel to another [`User`]. No other users may access the
/// channel. For multi-user "private channels", use a group.
///
/// [`User`]: struct.User.html
- Private(PrivateChannel),
+ Private(Arc<RwLock<PrivateChannel>>),
}
/// A container for guilds.
diff --git a/src/model/user.rs b/src/model/user.rs
index 8473623..fe2d3bb 100644
--- a/src/model/user.rs
+++ b/src/model/user.rs
@@ -24,6 +24,8 @@ use ::utils::builder::EditProfile;
use ::utils::decode_array;
#[cfg(feature="cache")]
+use std::sync::{Arc, RwLock};
+#[cfg(feature="cache")]
use ::client::CACHE;
impl CurrentUser {
@@ -87,7 +89,6 @@ impl CurrentUser {
}
/// Gets a list of guilds that the current user is in.
- #[inline]
pub fn guilds(&self) -> Result<Vec<GuildInfo>> {
rest::get_guilds(GuildPagination::After(GuildId(1)), 100)
}
@@ -191,15 +192,36 @@ impl User {
///
/// [`PrivateChannel`]: struct.PrivateChannel.html
/// [`User::dm`]: struct.User.html#method.dm
+ // A tale with Clippy:
+ //
+ // A person named Clippy once asked you to unlock a box and take something
+ // from it, but you never re-locked it, so you'll die and the universe will
+ // implode because the box must remain locked unless you're there, and you
+ // can't just borrow that item from it and take it with you forever.
+ //
+ // Instead what you do is unlock the box, take the item out of it, make a
+ // copy of said item, and then re-lock the box, and take your copy of the
+ // item with you.
+ //
+ // The universe is still fine, and nothing implodes.
+ //
+ // (AKA: Clippy is wrong and so we have to mark as allowing this lint.)
+ #[allow(let_and_return)]
pub fn direct_message(&self, content: &str)
-> Result<Message> {
let private_channel_id = feature_cache! {{
- let finding = CACHE.read()
- .unwrap()
- .private_channels
- .values()
- .find(|ch| ch.recipient.id == self.id)
- .map(|ch| ch.id);
+ let finding = {
+ let cache = CACHE.read().unwrap();
+
+ let finding = cache.private_channels
+ .values()
+ .map(|ch| ch.read().unwrap())
+ .find(|ch| ch.recipient.read().unwrap().id == self.id)
+ .map(|ch| ch.id)
+ .clone();
+
+ finding
+ };
if let Some(finding) = finding {
finding
@@ -331,7 +353,7 @@ impl User {
.unwrap()
.guilds
.get(&_guild_id)
- .map(|g| g.roles.contains_key(&role_id))
+ .map(|g| g.read().unwrap().roles.contains_key(&role_id))
.unwrap_or(false)
} else {
true
@@ -393,8 +415,8 @@ impl UserId {
/// Search the cache for the user with the Id.
#[cfg(feature="cache")]
- pub fn find(&self) -> Option<User> {
- CACHE.read().unwrap().get_user(*self).cloned()
+ pub fn find(&self) -> Option<Arc<RwLock<User>>> {
+ CACHE.read().unwrap().get_user(*self)
}
/// Gets a user by its Id over the REST API.
@@ -416,7 +438,7 @@ impl From<CurrentUser> for UserId {
impl From<Member> for UserId {
/// Gets the Id of a `Member`.
fn from(member: Member) -> UserId {
- member.user.id
+ member.user.read().unwrap().id
}
}
diff --git a/src/model/utils.rs b/src/model/utils.rs
index 58701bb..8de7dd2 100644
--- a/src/model/utils.rs
+++ b/src/model/utils.rs
@@ -1,4 +1,5 @@
use std::collections::{BTreeMap, HashMap};
+use std::sync::{Arc, RwLock};
use super::{
Channel,
ChannelId,
@@ -22,8 +23,6 @@ use ::utils::{decode_array, into_array};
use super::permissions::{self, Permissions};
#[cfg(feature="cache")]
use ::client::CACHE;
-#[cfg(feature="cache")]
-use ::ext::cache::ChannelRef;
#[macro_escape]
macro_rules! req {
@@ -93,7 +92,9 @@ pub fn decode_members(value: Value) -> Result<HashMap<UserId, Member>> {
let mut members = HashMap::new();
for member in decode_array(value, Member::decode)? {
- members.insert(member.user.id, member);
+ let user_id = member.user.read().unwrap().id;
+
+ members.insert(user_id, member);
}
Ok(members)
@@ -134,8 +135,8 @@ pub fn decode_private_channels(value: Value)
for private_channel in decode_array(value, Channel::decode)? {
let id = match private_channel {
- Channel::Group(ref group) => group.channel_id,
- Channel::Private(ref channel) => channel.id,
+ Channel::Group(ref group) => group.read().unwrap().channel_id,
+ Channel::Private(ref channel) => channel.read().unwrap().id,
Channel::Guild(_) => unreachable!("Guild private channel decode"),
};
@@ -217,11 +218,11 @@ pub fn decode_shards(value: Value) -> Result<[u64; 2]> {
])
}
-pub fn decode_users(value: Value) -> Result<HashMap<UserId, User>> {
+pub fn decode_users(value: Value) -> Result<HashMap<UserId, Arc<RwLock<User>>>> {
let mut users = HashMap::new();
for user in decode_array(value, User::decode)? {
- users.insert(user.id, user);
+ users.insert(user.id, Arc::new(RwLock::new(user)));
}
Ok(users)
@@ -306,10 +307,10 @@ pub fn user_has_perms(channel_id: ChannelId,
};
let guild_id = match channel {
- ChannelRef::Group(_) | ChannelRef::Private(_) => {
+ Channel::Group(_) | Channel::Private(_) => {
return Ok(permissions == permissions::MANAGE_MESSAGES);
},
- ChannelRef::Guild(channel) => channel.guild_id,
+ Channel::Guild(channel) => channel.read().unwrap().guild_id,
};
let guild = match cache.get_guild(guild_id) {
@@ -317,7 +318,7 @@ pub fn user_has_perms(channel_id: ChannelId,
None => return Err(Error::Client(ClientError::ItemMissing)),
};
- let perms = guild.permissions_for(channel_id, current_user.id);
+ let perms = guild.read().unwrap().permissions_for(channel_id, current_user.id);
permissions.remove(perms);