aboutsummaryrefslogtreecommitdiff
path: root/src/cache/cache_update.rs
diff options
context:
space:
mode:
authorZeyla Hellyer <[email protected]>2018-02-04 07:50:53 -0800
committerZeyla Hellyer <[email protected]>2018-02-04 07:54:31 -0800
commita9966371def331cd848f642e222627ee9decf354 (patch)
tree316c75854ddea79230f98b66708c3f815b836227 /src/cache/cache_update.rs
parentPartially revert the video url change (diff)
downloadserenity-a9966371def331cd848f642e222627ee9decf354.tar.xz
serenity-a9966371def331cd848f642e222627ee9decf354.zip
Rewrite the library to use Futures
Rewrites the library to use Futures. This rewrites all of the gateway, client, cache, most model methods, HTTP, and in a later commit the framework and voice. HTTP has been mostly rewritten to be ergonomic so that it can be used by the user, and has been upgraded to hyper v0.11. The gateway now uses `tokio-tungstenite` v0.4. The client isn't yet in a working state. The models now have a `client` optionally attached to them to make use of `http` and `cache`-utilizing methods. The client isn't needed, in the case that someone is using the library just to deserialize models. The cache is now built around `Rc`s and `RefCell`s, instead of `Arc`s and `RwLock`s. Refer to example 01 for a working example. Much of the library still doesn't work.
Diffstat (limited to 'src/cache/cache_update.rs')
-rw-r--r--src/cache/cache_update.rs642
1 files changed, 642 insertions, 0 deletions
diff --git a/src/cache/cache_update.rs b/src/cache/cache_update.rs
index a05cc3d..bdc0eea 100644
--- a/src/cache/cache_update.rs
+++ b/src/cache/cache_update.rs
@@ -1,3 +1,8 @@
+use model::prelude::*;
+use std::cell::RefCell;
+use std::collections::hash_map::{Entry, HashMap};
+use std::rc::Rc;
+use std::mem;
use super::Cache;
pub(crate) trait CacheUpdate {
@@ -5,3 +10,640 @@ pub(crate) trait CacheUpdate {
fn update(&mut self, &mut Cache) -> Option<Self::Output>;
}
+
+impl CacheUpdate for ChannelCreateEvent {
+ type Output = Channel;
+
+ fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> {
+ match self.channel {
+ Channel::Group(ref group) => {
+ let group = Rc::clone(group);
+
+ let channel_id = {
+ let mut writer = group.borrow_mut();
+
+ for (recipient_id, recipient) in &mut writer.recipients {
+ cache.update_user_entry(&recipient.borrow());
+
+ *recipient = Rc::clone(&cache.users[recipient_id]);
+ }
+
+ writer.channel_id
+ };
+
+ let old = cache.groups.insert(channel_id, group);
+
+ old.map(Channel::Group)
+ },
+ Channel::Guild(ref channel) => {
+ let (guild_id, channel_id) = {
+ let channel = channel.borrow();
+
+ (channel.guild_id, channel.id)
+ };
+
+ cache.channels.insert(channel_id, Rc::clone(channel));
+
+ cache
+ .guilds
+ .get_mut(&guild_id)
+ .and_then(|guild| {
+ let mut guild = guild.borrow_mut();
+
+ guild.channels.insert(channel_id, Rc::clone(channel))
+ })
+ .map(Channel::Guild)
+ },
+ Channel::Private(ref channel) => {
+ let channel_id = channel.borrow().id;
+
+ if let Some(channel) = cache.private_channels.get(&channel_id) {
+ return Some(Channel::Private(Rc::clone(channel)));
+ }
+
+ let channel = Rc::clone(channel);
+
+ let id = {
+ let mut writer = channel.borrow_mut();
+
+ let user_id = {
+ let user = writer.recipient.borrow();
+
+ cache.update_user_entry(&*user);
+
+ user.id
+ };
+
+ writer.recipient = Rc::clone(&cache.users[&user_id]);
+
+ writer.id
+ };
+
+ let old = cache.private_channels.insert(id, Rc::clone(&channel));
+ old.map(Channel::Private)
+ },
+ Channel::Category(ref category) => cache
+ .categories
+ .insert(category.borrow().id, Rc::clone(category))
+ .map(Channel::Category),
+ }
+ }
+}
+
+impl CacheUpdate for ChannelDeleteEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ match self.channel {
+ Channel::Guild(ref channel) => {
+ let (guild_id, channel_id) = {
+ let channel = channel.borrow();
+
+ (channel.guild_id, channel.id)
+ };
+
+ cache.channels.remove(&channel_id);
+
+ if let Some(guild) = cache.guilds.get(&guild_id) {
+ let mut guild = guild.borrow_mut();
+
+ guild.channels.remove(&channel_id);
+ }
+ },
+ Channel::Category(ref category) => {
+ let channel_id = category.borrow().id;
+
+ cache.categories.remove(&channel_id);
+ },
+ // We ignore these two due to the fact that the delete event for dms/groups
+ // will _not_ fire anymore.
+ Channel::Private(_) | Channel::Group(_) => unreachable!(),
+ };
+
+ None
+ }
+}
+
+impl CacheUpdate for ChannelPinsUpdateEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ if let Some(channel) = cache.channels.get(&self.channel_id) {
+ channel.borrow_mut().last_pin_timestamp = self.last_pin_timestamp;
+
+ return None;
+ }
+
+ if let Some(channel) = cache.private_channels.get_mut(&self.channel_id) {
+ channel.borrow_mut().last_pin_timestamp = self.last_pin_timestamp;
+
+ return None;
+ }
+
+ if let Some(group) = cache.groups.get_mut(&self.channel_id) {
+ group.borrow_mut().last_pin_timestamp = self.last_pin_timestamp;
+
+ return None;
+ }
+
+ None
+ }
+}
+
+impl CacheUpdate for ChannelRecipientAddEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ cache.update_user_entry(&self.user);
+ let user = Rc::clone(&cache.users[&self.user.id]);
+
+ cache.groups.get_mut(&self.channel_id).map(|group| {
+ group.borrow_mut().recipients.insert(self.user.id, user);
+ });
+
+ None
+ }
+}
+
+impl CacheUpdate for ChannelRecipientRemoveEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ if let Some(group) = cache.groups.get_mut(&self.channel_id) {
+ group.borrow_mut().recipients.remove(&self.user.id);
+ }
+
+ None
+ }
+}
+
+impl CacheUpdate for ChannelUpdateEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ match self.channel {
+ Channel::Group(ref group) => {
+ let (ch_id, no_recipients) = {
+ let group = group.borrow();
+
+ (group.channel_id, group.recipients.is_empty())
+ };
+
+ match cache.groups.entry(ch_id) {
+ Entry::Vacant(e) => {
+ e.insert(Rc::clone(group));
+ },
+ Entry::Occupied(mut e) => {
+ let mut dest = e.get_mut().borrow_mut();
+
+ if no_recipients {
+ let recipients = mem::replace(&mut dest.recipients, HashMap::new());
+
+ dest.clone_from(&group.borrow());
+
+ dest.recipients = recipients;
+ } else {
+ dest.clone_from(&group.borrow());
+ }
+ },
+ }
+ },
+ Channel::Guild(ref channel) => {
+ let (guild_id, channel_id) = {
+ let channel = channel.borrow();
+
+ (channel.guild_id, channel.id)
+ };
+
+ cache.channels.insert(channel_id, Rc::clone(channel));
+ cache.guilds.get_mut(&guild_id).map(|guild| {
+ let mut guild = guild.borrow_mut();
+
+ guild.channels.insert(channel_id, Rc::clone(channel))
+ });
+ },
+ Channel::Private(ref channel) => {
+ cache
+ .private_channels
+ .get_mut(&channel.borrow().id)
+ .map(|private| private.clone_from(channel));
+ },
+ Channel::Category(ref category) => {
+ cache
+ .categories
+ .get_mut(&category.borrow().id)
+ .map(|c| c.clone_from(category));
+ },
+ }
+
+ None
+ }
+}
+
+impl CacheUpdate for GuildCreateEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ cache.unavailable_guilds.remove(&self.guild.id);
+
+ let mut guild = self.guild.clone();
+
+ for (user_id, member) in &mut guild.members {
+ let mut member = member.borrow_mut();
+
+ cache.update_user_entry(&member.user.borrow_mut());
+
+ let user = Rc::clone(&cache.users[user_id]);
+ member.user = Rc::clone(&user);
+ }
+
+ cache.channels.extend(guild.channels.clone());
+ cache
+ .guilds
+ .insert(self.guild.id, Rc::new(RefCell::new(guild)));
+
+ None
+ }
+}
+
+impl CacheUpdate for GuildDeleteEvent {
+ type Output = Rc<RefCell<Guild>>;
+
+ fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> {
+ // Remove channel entries for the guild if the guild is found.
+ cache.guilds.remove(&self.guild.id).map(|guild| {
+ for channel_id in guild.borrow().channels.keys() {
+ cache.channels.remove(channel_id);
+ }
+
+ guild
+ })
+ }
+}
+
+impl CacheUpdate for GuildEmojisUpdateEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ if let Some(guild) = cache.guilds.get_mut(&self.guild_id) {
+ let mut guild = guild.borrow_mut();
+
+ guild.emojis.clone_from(&self.emojis);
+ }
+
+ None
+ }
+}
+
+impl CacheUpdate for GuildMemberAddEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ let user_id = self.member.user.borrow().id;
+ cache.update_user_entry(&self.member.user.borrow());
+
+ // Always safe due to being inserted above.
+ self.member.user = Rc::clone(&cache.users[&user_id]);
+
+ if let Some(guild) = cache.guilds.get_mut(&self.guild_id) {
+ let mut guild = guild.borrow_mut();
+
+ guild.member_count += 1;
+ guild.members.insert(user_id, Rc::new(RefCell::new(self.member.clone())));
+ }
+
+ None
+ }
+}
+
+impl CacheUpdate for GuildMemberRemoveEvent {
+ type Output = Rc<RefCell<Member>>;
+
+ fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> {
+ cache.guilds.get_mut(&self.guild_id).and_then(|guild| {
+ guild.try_borrow_mut().ok().and_then(|mut guild| {
+ guild.member_count -= 1;
+ guild.members.remove(&self.user.id)
+ })
+ })
+ }
+}
+
+impl CacheUpdate for GuildMemberUpdateEvent {
+ type Output = Member;
+
+ fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> {
+ cache.update_user_entry(&self.user);
+
+ if let Some(guild) = cache.guilds.get_mut(&self.guild_id) {
+ let mut guild = guild.borrow_mut();
+
+ let mut found = false;
+
+ let item = {
+ let member = guild.members.get(&self.user.id).and_then(|member| {
+ member.try_borrow_mut().ok()
+ });
+ if let Some(mut member) = member {
+ let item = Some(member.clone());
+
+ member.nick.clone_from(&self.nick);
+ member.roles.clone_from(&self.roles);
+ member.user.borrow_mut().clone_from(&self.user);
+
+ found = true;
+
+ item
+ } else {
+ None
+ }
+ };
+
+ if !found {
+ guild.members.insert(
+ self.user.id,
+ Rc::new(RefCell::new(Member {
+ client: None,
+ deaf: false,
+ guild_id: self.guild_id,
+ joined_at: None,
+ mute: false,
+ nick: self.nick.clone(),
+ roles: self.roles.clone(),
+ user: Rc::new(RefCell::new(self.user.clone())),
+ })),
+ );
+ }
+
+ item
+ } else {
+ None
+ }
+ }
+}
+
+impl CacheUpdate for GuildMembersChunkEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ for member in self.members.values() {
+ cache.update_user_entry(&member.user.borrow());
+ }
+
+ if let Some(guild) = cache.guilds.get(&self.guild_id) {
+ let mut guild = guild.borrow_mut();
+
+ guild.members.extend({
+ let mut m: HashMap<UserId, Rc<RefCell<Member>>> = HashMap::new();
+
+ for (id, member) in &self.members {
+ m.insert(*id, Rc::new(RefCell::new(member.clone())));
+ }
+
+ m
+ });
+ }
+
+ None
+ }
+}
+
+impl CacheUpdate for GuildRoleCreateEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ let guild = cache.guilds.get_mut(&self.guild_id).and_then(|guild| {
+ guild.try_borrow_mut().ok()
+ });
+
+ if let Some(mut guild) = guild {
+ guild
+ .roles
+ .insert(self.role.id, Rc::new(RefCell::new(self.role.clone())));
+ }
+
+ None
+ }
+}
+
+impl CacheUpdate for GuildRoleDeleteEvent {
+ type Output = Rc<RefCell<Role>>;
+
+ fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> {
+ cache
+ .guilds
+ .get_mut(&self.guild_id)
+ .and_then(|guild| {
+ let mut guild = guild.borrow_mut();
+
+ guild.roles.remove(&self.role_id)
+ })
+ }
+}
+
+impl CacheUpdate for GuildRoleUpdateEvent {
+ type Output = Role;
+
+ fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> {
+ cache.guilds.get_mut(&self.guild_id).and_then(|guild| {
+ let mut guild = guild.borrow_mut();
+
+ guild.roles.get_mut(&self.role.id).map(|role| {
+ mem::replace(&mut *role.borrow_mut(), self.role.clone())
+ })
+ })
+ }
+}
+
+impl CacheUpdate for GuildUnavailableEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ cache.unavailable_guilds.insert(self.guild_id);
+ cache.guilds.remove(&self.guild_id);
+
+ None
+ }
+}
+
+impl CacheUpdate for GuildUpdateEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ if let Some(guild) = cache.guilds.get(&self.guild.id) {
+ let mut guild = guild.try_borrow_mut().ok()?;
+
+ guild.afk_timeout = self.guild.afk_timeout;
+ guild.afk_channel_id.clone_from(&self.guild.afk_channel_id);
+ guild.icon.clone_from(&self.guild.icon);
+ guild.name.clone_from(&self.guild.name);
+ guild.owner_id.clone_from(&self.guild.owner_id);
+ guild.region.clone_from(&self.guild.region);
+ guild.roles.clone_from(&self.guild.roles);
+ guild.verification_level = self.guild.verification_level;
+ }
+
+ None
+ }
+}
+
+impl CacheUpdate for PresenceUpdateEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ let user_id = self.presence.user_id;
+
+ if let Some(user) = self.presence.user.as_mut() {
+ cache.update_user_entry(&user.borrow());
+ *user = Rc::clone(&cache.users[&user_id]);
+ }
+
+ if let Some(guild_id) = self.guild_id {
+ let guild = cache.guilds.get(&guild_id).and_then(|guild| {
+ guild.try_borrow_mut().ok()
+ });
+
+ if let Some(mut guild) = guild {
+ // If the member went offline, remove them from the presence list.
+ if self.presence.status == OnlineStatus::Offline {
+ guild.presences.remove(&self.presence.user_id);
+ } else {
+ guild
+ .presences
+ .insert(self.presence.user_id, Rc::new(RefCell::new(self.presence.clone())));
+ }
+
+ // Create a partial member instance out of the presence update
+ // data. This includes everything but `deaf`, `mute`, and
+ // `joined_at`.
+ if !guild.members.contains_key(&self.presence.user_id) {
+ if let Some(user) = self.presence.user.as_ref() {
+ let roles = self.roles.clone().unwrap_or_default();
+
+ guild.members.insert(self.presence.user_id, Rc::new(RefCell::new(Member {
+ client: None,
+ deaf: false,
+ guild_id: guild_id,
+ joined_at: None,
+ mute: false,
+ nick: self.presence.nick.clone(),
+ user: Rc::clone(&user),
+ roles,
+ })));
+ }
+ }
+ }
+ } else if self.presence.status == OnlineStatus::Offline {
+ cache.presences.remove(&self.presence.user_id);
+ } else {
+ cache
+ .presences
+ .insert(self.presence.user_id, Rc::new(RefCell::new(self.presence.clone())));
+ }
+
+ None
+ }
+}
+
+impl CacheUpdate for PresencesReplaceEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ cache.presences.extend({
+ let mut p: HashMap<UserId, Rc<RefCell<Presence>>> = HashMap::default();
+
+ for presence in &self.presences {
+ p.insert(presence.user_id, Rc::new(RefCell::new(presence.clone())));
+ }
+
+ p
+ });
+
+ None
+ }
+}
+
+impl CacheUpdate for ReadyEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ let mut ready = self.ready.clone();
+
+ for guild in ready.guilds {
+ match guild {
+ GuildStatus::Offline(unavailable) => {
+ cache.guilds.remove(&unavailable.id);
+ cache.unavailable_guilds.insert(unavailable.id);
+ },
+ GuildStatus::OnlineGuild(guild) => {
+ cache.unavailable_guilds.remove(&guild.id);
+ cache.guilds.insert(guild.id, Rc::new(RefCell::new(guild)));
+ },
+ GuildStatus::OnlinePartialGuild(_) => {},
+ }
+ }
+
+ // `ready.private_channels` will always be empty, and possibly be removed in the future.
+ // So don't handle it at all.
+
+ for (user_id, presence) in &mut ready.presences {
+ let mut presence = presence.borrow_mut();
+
+ if let Some(ref user) = presence.user {
+ cache.update_user_entry(&user.borrow());
+ }
+
+ presence.user = cache.users.get(user_id).cloned();
+ }
+
+ cache.presences.extend(ready.presences);
+ cache.shard_count = ready.shard.map_or(1, |s| s[1]);
+ cache.user = ready.user;
+
+ None
+ }
+}
+
+impl CacheUpdate for UserUpdateEvent {
+ type Output = CurrentUser;
+
+ fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> {
+ Some(mem::replace(&mut cache.user, self.current_user.clone()))
+ }
+}
+
+impl CacheUpdate for VoiceStateUpdateEvent {
+ type Output = ();
+
+ fn update(&mut self, cache: &mut Cache) -> Option<()> {
+ if let Some(guild_id) = self.guild_id {
+ if let Some(guild) = cache.guilds.get_mut(&guild_id) {
+ let mut guild = guild.borrow_mut();
+
+ if self.voice_state.channel_id.is_some() {
+ // Update or add to the voice state list
+ {
+ let finding = guild.voice_states.get_mut(&self.voice_state.user_id);
+
+ if let Some(srv_state) = finding {
+ srv_state.clone_from(&self.voice_state);
+
+ return None;
+ }
+ }
+
+ guild
+ .voice_states
+ .insert(self.voice_state.user_id, self.voice_state.clone());
+ } else {
+ // Remove the user from the voice state list
+ guild.voice_states.remove(&self.voice_state.user_id);
+ }
+ }
+
+ return None;
+ }
+
+ None
+ }
+}