aboutsummaryrefslogtreecommitdiff
path: root/src/model
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/model
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/model')
-rw-r--r--src/model/channel/attachment.rs87
-rw-r--r--src/model/channel/channel_category.rs90
-rw-r--r--src/model/channel/channel_id.rs553
-rw-r--r--src/model/channel/group.rs166
-rw-r--r--src/model/channel/guild_channel.rs208
-rw-r--r--src/model/channel/message.rs220
-rw-r--r--src/model/channel/mod.rs220
-rw-r--r--src/model/channel/private_channel.rs149
-rw-r--r--src/model/channel/reaction.rs99
-rw-r--r--src/model/error.rs4
-rw-r--r--src/model/event.rs620
-rw-r--r--src/model/gateway.rs22
-rw-r--r--src/model/guild/emoji.rs55
-rw-r--r--src/model/guild/guild_id.rs515
-rw-r--r--src/model/guild/member.rs267
-rw-r--r--src/model/guild/mod.rs558
-rw-r--r--src/model/guild/partial_guild.rs223
-rw-r--r--src/model/guild/role.rs51
-rw-r--r--src/model/invite.rs112
-rw-r--r--src/model/misc.rs73
-rw-r--r--src/model/mod.rs9
-rw-r--r--src/model/user.rs259
-rw-r--r--src/model/utils.rs153
-rw-r--r--src/model/webhook.rs96
24 files changed, 1618 insertions, 3191 deletions
diff --git a/src/model/channel/attachment.rs b/src/model/channel/attachment.rs
index 0530b6f..65df67e 100644
--- a/src/model/channel/attachment.rs
+++ b/src/model/channel/attachment.rs
@@ -1,10 +1,3 @@
-#[cfg(feature = "model")]
-use hyper::Client as HyperClient;
-#[cfg(feature = "model")]
-use internal::prelude::*;
-#[cfg(feature = "model")]
-use std::io::Read;
-
/// A file uploaded with a message. Not to be confused with [`Embed`]s.
///
/// [`Embed`]: struct.Embed.html
@@ -35,84 +28,4 @@ impl Attachment {
self.width
.and_then(|width| self.height.map(|height| (width, height)))
}
-
- /// Downloads the attachment, returning back a vector of bytes.
- ///
- /// # Examples
- ///
- /// Download all of the attachments associated with a [`Message`]:
- ///
- /// ```rust,no_run
- /// use serenity::model::prelude::*;
- /// use serenity::prelude::*;
- /// use std::env;
- /// use std::fs::File;
- /// use std::io::Write;
- /// use std::path::Path;
- ///
- /// struct Handler;
- ///
- /// impl EventHandler for Handler {
- /// fn message(&self, _: Context, message: Message) {
- /// for attachment in message.attachments {
- /// let content = match attachment.download() {
- /// Ok(content) => content,
- /// Err(why) => {
- /// println!("Error downloading attachment: {:?}", why);
- /// let _ = message.channel_id.say("Error downloading attachment");
- ///
- /// return;
- /// },
- /// };
- ///
- /// let mut file = match File::create(&attachment.filename) {
- /// Ok(file) => file,
- /// Err(why) => {
- /// println!("Error creating file: {:?}", why);
- /// let _ = message.channel_id.say("Error creating file");
- ///
- /// return;
- /// },
- /// };
- ///
- /// if let Err(why) = file.write(&content) {
- /// println!("Error writing to file: {:?}", why);
- ///
- /// return;
- /// }
- ///
- /// let _ = message.channel_id.say(&format!("Saved {:?}", attachment.filename));
- /// }
- /// }
- ///
- /// fn ready(&self, _: Context, ready: Ready) {
- /// println!("{} is connected!", ready.user.name);
- /// }
- /// }
- /// let token = env::var("DISCORD_TOKEN").expect("token in environment");
- /// let mut client = Client::new(&token, Handler).unwrap();
- ///
- /// client.start().unwrap();
- /// ```
- ///
- /// # Errors
- ///
- /// Returns an [`Error::Io`] when there is a problem reading the contents
- /// of the HTTP response.
- ///
- /// Returns an [`Error::Hyper`] when there is a problem retrieving the
- /// attachment.
- ///
- /// [`Error::Hyper`]: ../enum.Error.html#variant.Hyper
- /// [`Error::Io`]: ../enum.Error.html#variant.Io
- /// [`Message`]: struct.Message.html
- pub fn download(&self) -> Result<Vec<u8>> {
- let hyper = request_client!();
- let mut response = hyper.get(&self.url).send()?;
-
- let mut bytes = vec![];
- response.read_to_end(&mut bytes)?;
-
- Ok(bytes)
- }
}
diff --git a/src/model/channel/channel_category.rs b/src/model/channel/channel_category.rs
index 7a49c7e..451d550 100644
--- a/src/model/channel/channel_category.rs
+++ b/src/model/channel/channel_category.rs
@@ -1,11 +1,13 @@
+use client::Client;
+use futures::{Future, future};
use model::prelude::*;
+use std::rc::Rc;
+use ::FutureResult;
#[cfg(all(feature = "builder", feature = "model"))]
use builder::EditChannel;
-#[cfg(all(feature = "builder", feature = "model"))]
-use http;
-#[cfg(all(feature = "model", feature = "utils"))]
-use utils::{self as serenity_utils, VecMap};
+#[cfg(feature = "utils")]
+use utils as serenity_utils;
/// A category of [`GuildChannel`]s.
///
@@ -35,14 +37,17 @@ pub struct ChannelCategory {
///
/// [`GuildChannel`]: struct.GuildChannel.html
pub permission_overwrites: Vec<PermissionOverwrite>,
+ #[serde(skip)]
+ pub(crate) client: Option<Rc<Client>>,
}
-#[cfg(feature = "model")]
impl ChannelCategory {
/// Adds a permission overwrite to the category's channels.
#[inline]
- pub fn create_permission(&self, target: &PermissionOverwrite) -> Result<()> {
- self.id.create_permission(target)
+ pub fn create_permission(&self, target: &PermissionOverwrite) -> FutureResult<()> {
+ ftryopt!(self.client)
+ .http
+ .create_permission(self.id.0, target)
}
/// Deletes all permission overrides in the category from the channels.
@@ -51,23 +56,39 @@ impl ChannelCategory {
///
/// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
#[inline]
- pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> {
- self.id.delete_permission(permission_type)
+ pub fn delete_permission(&self, permission_type: PermissionOverwriteType)
+ -> FutureResult<()> {
+ let id = match permission_type {
+ PermissionOverwriteType::Member(id) => id.0,
+ PermissionOverwriteType::Role(id) => id.0,
+ };
+
+ let done = ftryopt!(self.client)
+ .http
+ .delete_permission(self.id.0, id);
+
+ Box::new(done)
}
/// Deletes this category.
#[inline]
- pub fn delete(&self) -> Result<()> {
+ pub fn delete(&self) -> FutureResult<()> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_CHANNELS;
- if !utils::user_has_perms(self.id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(client.cache.borrow().user_has_perms(self.id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.id.delete().map(|_| ())
+ let done = client.http.delete_channel(self.id.0).map(|_| ());
+
+ Box::new(done)
}
/// Modifies the category's settings, such as its position or name.
@@ -82,47 +103,22 @@ 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<()>
- where F: FnOnce(EditChannel) -> EditChannel {
+ pub fn edit<F: FnOnce(EditChannel) -> EditChannel>(&self, f: F)
+ -> FutureResult<GuildChannel> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_CHANNELS;
- if !utils::user_has_perms(self.id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(client.cache.borrow().user_has_perms(self.id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- let mut map = VecMap::new();
- map.insert("name", Value::String(self.name.clone()));
- map.insert("position", Value::Number(Number::from(self.position)));
- map.insert("type", Value::String(self.kind.name().to_string()));
-
- let map = serenity_utils::vecmap_to_json_map(f(EditChannel(map)).0);
-
- http::edit_channel(self.id.0, &map).map(|channel| {
- let GuildChannel {
- id,
- category_id,
- permission_overwrites,
- nsfw,
- name,
- position,
- kind,
- ..
- } = channel;
-
- *self = ChannelCategory {
- id,
- category_id,
- permission_overwrites,
- nsfw,
- name,
- position,
- kind,
- };
- ()
- })
+ ftryopt!(self.client).http.edit_channel(self.id.0, f)
}
#[cfg(feature = "utils")]
diff --git a/src/model/channel/channel_id.rs b/src/model/channel/channel_id.rs
index 00dc1f2..fc6d748 100644
--- a/src/model/channel/channel_id.rs
+++ b/src/model/channel/channel_id.rs
@@ -1,550 +1,13 @@
-use internal::RwLockExt;
use model::prelude::*;
-#[cfg(feature = "model")]
-use std::borrow::Cow;
-#[cfg(feature = "model")]
-use std::fmt::Write as FmtWrite;
-#[cfg(feature = "model")]
-use builder::{CreateMessage, EditChannel, EditMessage, GetMessages};
-#[cfg(all(feature = "cache", feature = "model"))]
-use CACHE;
-#[cfg(feature = "model")]
-use http::{self, AttachmentType};
-#[cfg(feature = "model")]
-use utils;
-
-#[cfg(feature = "model")]
-impl ChannelId {
- /// Broadcasts that the current user is typing to a channel for the next 5
- /// seconds.
- ///
- /// After 5 seconds, another request must be made to continue broadcasting
- /// that the current user is typing.
- ///
- /// This should rarely be used for bots, and should likely only be used for
- /// signifying that a long-running command is still being executed.
- ///
- /// **Note**: Requires the [Send Messages] permission.
- ///
- /// # Examples
- ///
- /// ```rust,ignore
- /// use serenity::model::ChannelId;
- ///
- /// let _successful = ChannelId(7).broadcast_typing();
- /// ```
- ///
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- #[inline]
- pub fn broadcast_typing(&self) -> Result<()> { http::broadcast_typing(self.0) }
-
- /// Creates a [permission overwrite][`PermissionOverwrite`] for either a
- /// single [`Member`] or [`Role`] within the channel.
- ///
- /// Refer to the documentation for [`GuildChannel::create_permission`] for
- /// more information.
- ///
- /// Requires the [Manage Channels] permission.
- ///
- /// [`GuildChannel::create_permission`]: struct.GuildChannel.html#method.create_permission
- /// [`Member`]: struct.Member.html
- /// [`PermissionOverwrite`]: struct.PermissionOverwrite.html
- /// [`Role`]: struct.Role.html
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
- pub fn create_permission(&self, target: &PermissionOverwrite) -> Result<()> {
- let (id, kind) = match target.kind {
- PermissionOverwriteType::Member(id) => (id.0, "member"),
- PermissionOverwriteType::Role(id) => (id.0, "role"),
- };
-
- let map = json!({
- "allow": target.allow.bits(),
- "deny": target.deny.bits(),
- "id": id,
- "type": kind,
- });
-
- http::create_permission(self.0, id, &map)
- }
-
- /// React to a [`Message`] with a custom [`Emoji`] or unicode character.
- ///
- /// [`Message::react`] may be a more suited method of reacting in most
- /// cases.
- ///
- /// Requires the [Add Reactions] permission, _if_ the current user is the
- /// first user to perform a react with a certain emoji.
- ///
- /// [`Emoji`]: struct.Emoji.html
- /// [`Message`]: struct.Message.html
- /// [`Message::react`]: struct.Message.html#method.react
- /// [Add Reactions]: permissions/constant.ADD_REACTIONS.html
- #[inline]
- pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R) -> Result<()>
- where M: Into<MessageId>, R: Into<ReactionType> {
- http::create_reaction(self.0, message_id.into().0, &reaction_type.into())
- }
-
- /// Deletes this channel, returning the channel on a successful deletion.
- #[inline]
- pub fn delete(&self) -> Result<Channel> { http::delete_channel(self.0) }
-
- /// Deletes a [`Message`] given its Id.
- ///
- /// Refer to [`Message::delete`] for more information.
- ///
- /// Requires the [Manage Messages] permission, if the current user is not
- /// the author of the message.
- ///
- /// [`Message`]: struct.Message.html
- /// [`Message::delete`]: struct.Message.html#method.delete
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- #[inline]
- pub fn delete_message<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
- http::delete_message(self.0, message_id.into().0)
- }
-
- /// Deletes all messages by Ids from the given vector in the given channel.
- ///
- /// Refer to the documentation for [`Channel::delete_messages`] for more
- /// information.
- ///
- /// Requires the [Manage Messages] permission.
- ///
- /// **Note**: Messages that are older than 2 weeks can't be deleted using
- /// this method.
- ///
- /// # Errors
- ///
- /// Returns [`ModelError::BulkDeleteAmount`] if an attempt was made to
- /// delete either 0 or more than 100 messages.
- ///
- /// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages
- /// [`ModelError::BulkDeleteAmount`]: ../enum.ModelError.html#variant.BulkDeleteAmount
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>(&self, message_ids: It) -> Result<()> {
- let ids = message_ids
- .into_iter()
- .map(|message_id| message_id.as_ref().0)
- .collect::<Vec<u64>>();
- let len = ids.len();
-
- if len == 0 || len > 100 {
- return Err(Error::Model(ModelError::BulkDeleteAmount));
- } else if ids.len() == 1 {
- self.delete_message(ids[0])
- } else {
- let map = json!({ "messages": ids });
-
- http::delete_messages(self.0, &map)
- }
- }
-
- /// Deletes all permission overrides in the channel from a member or role.
- ///
- /// **Note**: Requires the [Manage Channel] permission.
- ///
- /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
- pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> {
- http::delete_permission(
- self.0,
- match permission_type {
- PermissionOverwriteType::Member(id) => id.0,
- PermissionOverwriteType::Role(id) => id.0,
- },
- )
- }
-
- /// Deletes the given [`Reaction`] from the channel.
- ///
- /// **Note**: Requires the [Manage Messages] permission, _if_ the current
- /// user did not perform the reaction.
- ///
- /// [`Reaction`]: struct.Reaction.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- pub fn delete_reaction<M, R>(&self,
- message_id: M,
- user_id: Option<UserId>,
- reaction_type: R)
- -> Result<()>
- where M: Into<MessageId>, R: Into<ReactionType> {
- http::delete_reaction(
- self.0,
- message_id.into().0,
- user_id.map(|uid| uid.0),
- &reaction_type.into(),
- )
- }
-
-
- /// Edits the settings of a [`Channel`], optionally setting new values.
- ///
- /// Refer to `EditChannel`'s documentation for its methods.
- ///
- /// Requires the [Manage Channel] permission.
- ///
- /// # Examples
- ///
- /// Change a voice channel's name and bitrate:
- ///
- /// ```rust,ignore
- /// // assuming a `channel_id` has been bound
- ///
- /// channel_id.edit(|c| c.name("test").bitrate(64000));
- /// ```
- ///
- /// [`Channel`]: enum.Channel.html
- /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
- #[cfg(feature = "utils")]
- #[inline]
- pub fn edit<F: FnOnce(EditChannel) -> EditChannel>(&self, f: F) -> Result<GuildChannel> {
- let map = utils::vecmap_to_json_map(f(EditChannel::default()).0);
-
- http::edit_channel(self.0, &map)
- }
-
- /// Edits a [`Message`] in the channel given its Id.
- ///
- /// Message editing preserves all unchanged message data.
- ///
- /// Refer to the documentation for [`EditMessage`] for more information
- /// regarding message restrictions and requirements.
- ///
- /// **Note**: Requires that the current user be the author of the message.
- ///
- /// # Errors
- ///
- /// Returns a [`ModelError::MessageTooLong`] if the content of the message
- /// is over the [`the limit`], containing the number of unicode code points
- /// over the limit.
- ///
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- /// [`EditMessage`]: ../builder/struct.EditMessage.html
- /// [`Message`]: struct.Message.html
- /// [`the limit`]: ../builder/struct.EditMessage.html#method.content
- #[cfg(feature = "utils")]
- pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
- where F: FnOnce(EditMessage) -> EditMessage, M: Into<MessageId> {
- let msg = f(EditMessage::default());
-
- if let Some(content) = msg.0.get(&"content") {
- if let Value::String(ref content) = *content {
- if let Some(length_over) = Message::overflow_length(content) {
- return Err(Error::Model(ModelError::MessageTooLong(length_over)));
- }
- }
- }
-
- let map = utils::vecmap_to_json_map(msg.0);
-
- http::edit_message(self.0, message_id.into().0, &Value::Object(map))
- }
-
- /// Search the cache for the channel with the Id.
- #[cfg(feature = "cache")]
- pub fn find(&self) -> Option<Channel> { CACHE.read().channel(*self) }
-
- /// Search the cache for the channel. If it can't be found, the channel is
- /// requested over REST.
- pub fn get(&self) -> Result<Channel> {
- #[cfg(feature = "cache")]
- {
- if let Some(channel) = CACHE.read().channel(*self) {
- return Ok(channel);
- }
- }
-
- http::get_channel(self.0)
- }
-
- /// Gets all of the channel's invites.
- ///
- /// Requires the [Manage Channels] permission.
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
- #[inline]
- pub fn invites(&self) -> Result<Vec<RichInvite>> { http::get_channel_invites(self.0) }
-
- /// Gets a message from the channel.
- ///
- /// Requires the [Read Message History] permission.
- ///
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
- #[inline]
- pub fn message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> {
- http::get_message(self.0, message_id.into().0)
- .map(|mut msg| {
- msg.transform_content();
-
- msg
- })
- }
-
- /// Gets messages from the channel.
- ///
- /// Refer to [`Channel::messages`] for more information.
- ///
- /// Requires the [Read Message History] permission.
- ///
- /// [`Channel::messages`]: enum.Channel.html#method.messages
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
- pub fn messages<F>(&self, f: F) -> Result<Vec<Message>>
- where F: FnOnce(GetMessages) -> GetMessages {
- let mut map = f(GetMessages::default()).0;
- let mut query = format!("?limit={}", map.remove(&"limit").unwrap_or(50));
-
- if let Some(after) = map.remove(&"after") {
- write!(query, "&after={}", after)?;
- } else if let Some(around) = map.remove(&"around") {
- write!(query, "&around={}", around)?;
- } else if let Some(before) = map.remove(&"before") {
- write!(query, "&before={}", before)?;
- }
-
- http::get_messages(self.0, &query).map(|msgs| {
- msgs.into_iter()
- .map(|mut msg| {
- msg.transform_content();
-
- msg
- })
- .collect::<Vec<Message>>()
- })
- }
-
- /// Returns the name of whatever channel this id holds.
- #[cfg(feature = "model")]
- pub fn name(&self) -> Option<String> {
- use self::Channel::*;
-
- let finding = feature_cache! {{
- Some(self.find())
- } else {
- None
- }};
-
- let channel = if let Some(Some(c)) = finding {
- c
- } else {
- return None;
- };
-
- Some(match channel {
- Guild(channel) => channel.read().name().to_string(),
- Group(channel) => match channel.read().name() {
- Cow::Borrowed(name) => name.to_string(),
- Cow::Owned(name) => name,
- },
- Category(category) => category.read().name().to_string(),
- Private(channel) => channel.read().name(),
- })
- }
-
- /// Pins a [`Message`] to the channel.
- ///
- /// [`Message`]: struct.Message.html
- #[inline]
- pub fn pin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
- http::pin_message(self.0, message_id.into().0)
- }
-
- /// Gets the list of [`Message`]s which are pinned to the channel.
- ///
- /// [`Message`]: struct.Message.html
- #[inline]
- pub fn pins(&self) -> Result<Vec<Message>> { http::get_pins(self.0) }
-
- /// Gets the list of [`User`]s who have reacted to a [`Message`] with a
- /// certain [`Emoji`].
- ///
- /// Refer to [`Channel::reaction_users`] for more information.
- ///
- /// **Note**: Requires the [Read Message History] permission.
- ///
- /// [`Channel::reaction_users`]: enum.Channel.html#method.reaction_users
- /// [`Emoji`]: struct.Emoji.html
- /// [`Message`]: struct.Message.html
- /// [`User`]: struct.User.html
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
- pub fn reaction_users<M, R, U>(&self,
- message_id: M,
- reaction_type: R,
- limit: Option<u8>,
- after: U,
- ) -> Result<Vec<User>> where M: Into<MessageId>,
- R: Into<ReactionType>,
- U: Into<Option<UserId>> {
- let limit = limit.map_or(50, |x| if x > 100 { 100 } else { x });
-
- http::get_reaction_users(
- self.0,
- message_id.into().0,
- &reaction_type.into(),
- limit,
- after.into().map(|x| x.0),
- )
- }
-
- /// Sends a message with just the given message content in the channel.
- ///
- /// # Errors
- ///
- /// Returns a [`ModelError::MessageTooLong`] if the content of the message
- /// is over the above limit, containing the number of unicode code points
- /// over the limit.
- ///
- /// [`ChannelId`]: struct.ChannelId.html
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- #[inline]
- pub fn say<D: ::std::fmt::Display>(&self, content: D) -> Result<Message> {
- self.send_message(|m| m.content(content))
- }
-
- /// Sends a file along with optional message contents. The filename _must_
- /// be specified.
- ///
- /// Message contents may be passed by using the [`CreateMessage::content`]
- /// method.
- ///
- /// An embed can _not_ be sent when sending a file. If you set one, it will
- /// be automatically removed.
- ///
- /// The [Attach Files] and [Send Messages] permissions are required.
- ///
- /// **Note**: Message contents must be under 2000 unicode code points.
- ///
- /// # Examples
- ///
- /// Send files with the paths `/path/to/file.jpg` and `/path/to/file2.jpg`:
- ///
- /// ```rust,no_run
- /// use serenity::model::id::ChannelId;
- ///
- /// let channel_id = ChannelId(7);
- ///
- /// let paths = vec!["/path/to/file.jpg", "path/to/file2.jpg"];
- ///
- /// let _ = channel_id.send_files(paths, |m| m.content("a file"));
- /// ```
- ///
- /// Send files using `File`:
- ///
- /// ```rust,no_run
- /// use serenity::model::id::ChannelId;
- /// use std::fs::File;
- ///
- /// let channel_id = ChannelId(7);
- ///
- /// let f1 = File::open("my_file.jpg").unwrap();
- /// let f2 = File::open("my_file2.jpg").unwrap();
- ///
- /// let files = vec![(&f1, "my_file.jpg"), (&f2, "my_file2.jpg")];
- ///
- /// let _ = channel_id.send_files(files, |m| m.content("a file"));
- /// ```
- ///
- /// # Errors
- ///
- /// If the content of the message is over the above limit, then a
- /// [`ClientError::MessageTooLong`] will be returned, containing the number
- /// of unicode code points over the limit.
- ///
- /// Returns an
- /// [`HttpError::InvalidRequest(PayloadTooLarge)`][`HttpError::InvalidRequest`]
- /// if the file is too large to send.
- ///
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [`HttpError::InvalidRequest`]: ../http/enum.HttpError.html#variant.InvalidRequest
- /// [`CreateMessage::content`]: ../utils/builder/struct.CreateMessage.html#method.content
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [Attach Files]: permissions/constant.ATTACH_FILES.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- #[cfg(feature = "utils")]
- pub fn send_files<'a, F, T, It: IntoIterator<Item=T>>(&self, files: It, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage, T: Into<AttachmentType<'a>> {
- let mut msg = f(CreateMessage::default());
-
- if let Some(content) = msg.0.get(&"content") {
- if let Value::String(ref content) = *content {
- if let Some(length_over) = Message::overflow_length(content) {
- return Err(Error::Model(ModelError::MessageTooLong(length_over)));
- }
- }
- }
-
- let _ = msg.0.remove(&"embed");
- let map = utils::vecmap_to_json_map(msg.0);
-
- http::send_files(self.0, files, map)
- }
-
- /// Sends a message to the channel.
- ///
- /// Refer to the documentation for [`CreateMessage`] for more information
- /// regarding message restrictions and requirements.
- ///
- /// Requires the [Send Messages] permission.
- ///
- /// **Note**: Message contents must be under 2000 unicode code points.
- ///
- /// # Errors
- ///
- /// Returns a [`ModelError::MessageTooLong`] if the content of the message
- /// is over the above limit, containing the number of unicode code points
- /// over the limit.
- ///
- /// [`Channel`]: enum.Channel.html
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- /// [`CreateMessage`]: ../builder/struct.CreateMessage.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- #[cfg(feature = "utils")]
- pub fn send_message<F>(&self, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage {
- let msg = f(CreateMessage::default());
- let map = utils::vecmap_to_json_map(msg.0);
-
- Message::check_content_length(&map)?;
- Message::check_embed_length(&map)?;
-
- let message = http::send_message(self.0, &Value::Object(map))?;
-
- if let Some(reactions) = msg.1 {
- for reaction in reactions {
- self.create_reaction(message.id, reaction)?;
- }
- }
-
- Ok(message)
- }
-
- /// Unpins a [`Message`] in the channel given by its Id.
- ///
- /// Requires the [Manage Messages] permission.
- ///
- /// [`Message`]: struct.Message.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- #[inline]
- pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
- http::unpin_message(self.0, message_id.into().0)
- }
-
- /// Retrieves the channel's webhooks.
- ///
- /// **Note**: Requires the [Manage Webhooks] permission.
- ///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
- #[inline]
- pub fn webhooks(&self) -> Result<Vec<Webhook>> { http::get_channel_webhooks(self.0) }
-}
-
impl From<Channel> for ChannelId {
/// Gets the Id of a `Channel`.
fn from(channel: Channel) -> ChannelId {
match channel {
- Channel::Group(group) => group.with(|g| g.channel_id),
- Channel::Guild(ch) => ch.with(|c| c.id),
- Channel::Private(ch) => ch.with(|c| c.id),
- Channel::Category(ch) => ch.with(|c| c.id),
+ Channel::Group(group) => group.borrow().channel_id,
+ Channel::Guild(ch) => ch.borrow().id,
+ Channel::Private(ch) => ch.borrow().id,
+ Channel::Category(ch) => ch.borrow().id,
}
}
}
@@ -553,10 +16,10 @@ impl<'a> From<&'a Channel> for ChannelId {
/// Gets the Id of a `Channel`.
fn from(channel: &Channel) -> ChannelId {
match *channel {
- Channel::Group(ref group) => group.with(|g| g.channel_id),
- Channel::Guild(ref ch) => ch.with(|c| c.id),
- Channel::Private(ref ch) => ch.with(|c| c.id),
- Channel::Category(ref ch) => ch.with(|c| c.id),
+ Channel::Group(ref group) => group.borrow().channel_id,
+ Channel::Guild(ref ch) => ch.borrow().id,
+ Channel::Private(ref ch) => ch.borrow().id,
+ Channel::Category(ref ch) => ch.borrow().id,
}
}
}
diff --git a/src/model/channel/group.rs b/src/model/channel/group.rs
index 61fe67e..f55a9dd 100644
--- a/src/model/channel/group.rs
+++ b/src/model/channel/group.rs
@@ -1,13 +1,14 @@
use chrono::{DateTime, FixedOffset};
+use client::Client;
+use futures::{Future, future};
use model::prelude::*;
+use std::cell::RefCell;
+use std::rc::Rc;
+use ::FutureResult;
-#[cfg(feature = "model")]
+// #[cfg(feature = "model")]
use builder::{CreateMessage, EditMessage, GetMessages};
#[cfg(feature = "model")]
-use http::{self, AttachmentType};
-#[cfg(feature = "model")]
-use internal::RwLockExt;
-#[cfg(feature = "model")]
use std::borrow::Cow;
#[cfg(feature = "model")]
use std::fmt::Write as FmtWrite;
@@ -35,10 +36,11 @@ pub struct Group {
/// A map of the group's recipients.
#[serde(deserialize_with = "deserialize_users",
serialize_with = "serialize_users")]
- pub recipients: HashMap<UserId, Arc<RwLock<User>>>,
+ pub recipients: HashMap<UserId, Rc<RefCell<User>>>,
+ #[serde(skip)]
+ pub(crate) client: Option<Rc<Client>>,
}
-#[cfg(feature = "model")]
impl Group {
/// Adds the given user to the group. If the user is already in the group,
/// then nothing is done.
@@ -49,20 +51,24 @@ impl Group {
/// user.
///
/// [`http::add_group_recipient`]: ../http/fn.add_group_recipient.html
- pub fn add_recipient<U: Into<UserId>>(&self, user: U) -> Result<()> {
+ pub fn add_recipient<U: Into<UserId>>(&self, user: U) -> FutureResult<()> {
let user = user.into();
// If the group already contains the recipient, do nothing.
if self.recipients.contains_key(&user) {
- return Ok(());
+ return Box::new(future::ok(()));
}
- http::add_group_recipient(self.channel_id.0, user.0)
+ ftryopt!(self.client)
+ .http
+ .add_group_recipient(self.channel_id.0, user.0)
}
/// Broadcasts that the current user is typing in the group.
#[inline]
- pub fn broadcast_typing(&self) -> Result<()> { self.channel_id.broadcast_typing() }
+ pub fn broadcast_typing(&self) -> FutureResult<()> {
+ ftryopt!(self.client).http.broadcast_typing(self.channel_id.0)
+ }
/// React to a [`Message`] with a custom [`Emoji`] or unicode character.
///
@@ -77,9 +83,14 @@ impl Group {
/// [`Message::react`]: struct.Message.html#method.react
/// [Add Reactions]: permissions/constant.ADD_REACTIONS.html
#[inline]
- pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R) -> Result<()>
+ pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R)
+ -> FutureResult<()>
where M: Into<MessageId>, R: Into<ReactionType> {
- self.channel_id.create_reaction(message_id, reaction_type)
+ ftryopt!(self.client).http.create_reaction(
+ self.channel_id.0,
+ message_id.into().0,
+ &reaction_type.into(),
+ )
}
/// Deletes all messages by Ids from the given vector in the channel.
@@ -100,8 +111,14 @@ impl Group {
/// [`ModelError::BulkDeleteAmount`]: ../enum.ModelError.html#variant.BulkDeleteAmount
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
#[inline]
- pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>(&self, message_ids: It) -> Result<()> {
- self.channel_id.delete_messages(message_ids)
+ pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>(
+ &self,
+ message_ids: It,
+ ) -> FutureResult<()> {
+ ftryopt!(self.client).http.delete_messages(
+ self.channel_id.0,
+ message_ids,
+ )
}
/// Deletes all permission overrides in the channel from a member
@@ -111,8 +128,17 @@ impl Group {
///
/// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
#[inline]
- pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> {
- self.channel_id.delete_permission(permission_type)
+ pub fn delete_permission(&self, permission_type: PermissionOverwriteType)
+ -> FutureResult<()> {
+ let overwrite_id = match permission_type {
+ PermissionOverwriteType::Member(id) => id.0,
+ PermissionOverwriteType::Role(id) => id.0,
+ };
+
+ ftryopt!(self.client).http.delete_permission(
+ self.channel_id.0,
+ overwrite_id,
+ )
}
/// Deletes the given [`Reaction`] from the channel.
@@ -123,14 +149,18 @@ impl Group {
/// [`Reaction`]: struct.Reaction.html
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
#[inline]
- pub fn delete_reaction<M, R>(&self,
- message_id: M,
- user_id: Option<UserId>,
- reaction_type: R)
- -> Result<()>
- where M: Into<MessageId>, R: Into<ReactionType> {
- self.channel_id
- .delete_reaction(message_id, user_id, reaction_type)
+ pub fn delete_reaction<M, R>(
+ &self,
+ message_id: M,
+ user_id: Option<UserId>,
+ reaction_type: R,
+ ) -> FutureResult<()> where M: Into<MessageId>, R: Into<ReactionType> {
+ ftryopt!(self.client).http.delete_reaction(
+ self.channel_id.0,
+ message_id.into().0,
+ user_id.map(|x| x.0),
+ &reaction_type.into(),
+ )
}
/// Edits a [`Message`] in the channel given its Id.
@@ -153,9 +183,14 @@ impl Group {
/// [`Message`]: struct.Message.html
/// [`the limit`]: ../builder/struct.EditMessage.html#method.content
#[inline]
- pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
+ pub fn edit_message<F, M>(&self, message_id: M, f: F)
+ -> FutureResult<Message>
where F: FnOnce(EditMessage) -> EditMessage, M: Into<MessageId> {
- self.channel_id.edit_message(message_id, f)
+ ftryopt!(self.client).http.edit_message(
+ self.channel_id.0,
+ message_id.into().0,
+ f,
+ )
}
/// Returns the formatted URI of the group's icon if one exists.
@@ -178,7 +213,9 @@ impl Group {
/// Leaves the group.
#[inline]
- pub fn leave(&self) -> Result<Group> { http::leave_group(self.channel_id.0) }
+ pub fn leave(&self) -> FutureResult<()> {
+ ftryopt!(self.client).http.leave_group(self.channel_id.0)
+ }
/// Gets a message from the channel.
///
@@ -186,8 +223,8 @@ impl Group {
///
/// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
#[inline]
- pub fn message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> {
- self.channel_id.message(message_id)
+ pub fn message<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<Message> {
+ ftryopt!(self.client).http.get_message(self.channel_id.0, message_id.into().0)
}
/// Gets messages from the channel.
@@ -196,9 +233,9 @@ impl Group {
///
/// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
#[inline]
- pub fn messages<F>(&self, f: F) -> Result<Vec<Message>>
- where F: FnOnce(GetMessages) -> GetMessages {
- self.channel_id.messages(f)
+ pub fn messages<'a, F: FnOnce(GetMessages) -> GetMessages>(&'a self, f: F)
+ -> Box<Future<Item = Vec<Message>, Error = Error> + 'a> {
+ ftryopt!(self.client).http.get_messages(self.channel_id.0, f)
}
/// Generates a name for the group.
@@ -211,12 +248,14 @@ impl Group {
Some(ref name) => Cow::Borrowed(name),
None => {
let mut name = match self.recipients.values().nth(0) {
- Some(recipient) => recipient.with(|c| c.name.clone()),
+ Some(recipient) => recipient.borrow().name.clone(),
None => return Cow::Borrowed("Empty Group"),
};
for recipient in self.recipients.values().skip(1) {
- let _ = write!(name, ", {}", recipient.with(|r| r.name.clone()));
+ let recipient = recipient.borrow();
+
+ let _ = write!(name, ", {}", recipient.name);
}
Cow::Owned(name)
@@ -226,7 +265,9 @@ impl Group {
/// Retrieves the list of messages that have been pinned in the group.
#[inline]
- pub fn pins(&self) -> Result<Vec<Message>> { self.channel_id.pins() }
+ pub fn pins(&self) -> FutureResult<Vec<Message>> {
+ ftryopt!(self.client).http.get_pins(self.channel_id.0)
+ }
/// Gets the list of [`User`]s who have reacted to a [`Message`] with a
/// certain [`Emoji`].
@@ -247,25 +288,34 @@ impl Group {
reaction_type: R,
limit: Option<u8>,
after: U,
- ) -> Result<Vec<User>> where M: Into<MessageId>,
- R: Into<ReactionType>,
- U: Into<Option<UserId>> {
- self.channel_id.reaction_users(message_id, reaction_type, limit, after)
+ ) -> FutureResult<Vec<User>>
+ where M: Into<MessageId>, R: Into<ReactionType>, U: Into<Option<UserId>> {
+ ftryopt!(self.client).http.get_reaction_users(
+ self.channel_id.0,
+ message_id.into().0,
+ &reaction_type.into(),
+ limit,
+ after.into().map(|x| x.0),
+ )
}
/// Removes a recipient from the group. If the recipient is already not in
/// the group, then nothing is done.
///
/// **Note**: This is only available to the group owner.
- pub fn remove_recipient<U: Into<UserId>>(&self, user: U) -> Result<()> {
+ pub fn remove_recipient<U>(&self, user: U) -> FutureResult<()>
+ where U: Into<UserId> {
let user = user.into();
// If the group does not contain the recipient already, do nothing.
if !self.recipients.contains_key(&user) {
- return Ok(());
+ return Box::new(future::ok(()));
}
- http::remove_group_recipient(self.channel_id.0, user.0)
+ ftryopt!(self.client).http.remove_group_recipient(
+ self.channel_id.0,
+ user.0,
+ )
}
/// Sends a message with just the given message content in the channel.
@@ -279,7 +329,10 @@ impl Group {
/// [`ChannelId`]: ../model/id/struct.ChannelId.html
/// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
#[inline]
- pub fn say(&self, content: &str) -> Result<Message> { self.channel_id.say(content) }
+ pub fn say(&self, content: &str) -> FutureResult<Message> {
+ ftryopt!(self.client).http.send_message(self.channel_id.0, |f| f
+ .content(content))
+ }
/// Sends (a) file(s) along with optional message contents.
///
@@ -299,11 +352,15 @@ impl Group {
/// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
/// [Attach Files]: permissions/constant.ATTACH_FILES.html
/// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- #[inline]
- pub fn send_files<'a, F, T, It: IntoIterator<Item=T>>(&self, files: It, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage, T: Into<AttachmentType<'a>> {
- self.channel_id.send_files(files, f)
- }
+ // todo
+ // #[inline]
+ // pub fn send_files<'a, F, T, It>(&self, files: It, f: F)
+ // -> FutureResult<Message>
+ // where F: FnOnce(CreateMessage) -> CreateMessage,
+ // T: Into<AttachmentType<'a>>,
+ // It: IntoIterator<Item=T> {
+ // ftryopt!(self.client).http.send_files(self.channel_id.0, files, f)
+ // }
/// Sends a message to the group with the given content.
///
@@ -315,8 +372,9 @@ impl Group {
/// [`CreateMessage`]: ../builder/struct.CreateMessage.html
/// [Send Messages]: permissions/constant.SEND_MESSAGES.html
#[inline]
- pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) -> Result<Message> {
- self.channel_id.send_message(f)
+ pub fn send_message<F>(&self, f: F) -> FutureResult<Message>
+ where F: FnOnce(CreateMessage) -> CreateMessage {
+ ftryopt!(self.client).http.send_message(self.channel_id.0, f)
}
/// Unpins a [`Message`] in the channel given by its Id.
@@ -326,7 +384,9 @@ impl Group {
/// [`Message`]: struct.Message.html
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
#[inline]
- pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
- self.channel_id.unpin(message_id)
+ pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<()> {
+ ftryopt!(self.client)
+ .http
+ .unpin_message(self.channel_id.0, message_id.into().0)
}
}
diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs
index 9b7d431..ea99377 100644
--- a/src/model/channel/guild_channel.rs
+++ b/src/model/channel/guild_channel.rs
@@ -1,20 +1,18 @@
use chrono::{DateTime, FixedOffset};
+use futures::{Future, future};
use model::prelude::*;
+use std::cell::RefCell;
+use super::super::WrappedClient;
+use ::FutureResult;
-#[cfg(all(feature = "cache", feature = "model"))]
-use CACHE;
#[cfg(feature = "model")]
use builder::{CreateInvite, CreateMessage, EditChannel, EditMessage, GetMessages};
-#[cfg(feature = "model")]
-use http::{self, AttachmentType};
#[cfg(all(feature = "cache", feature = "model"))]
use internal::prelude::*;
#[cfg(feature = "model")]
use std::fmt::{Display, Formatter, Result as FmtResult};
-#[cfg(feature = "model")]
-use std::mem;
#[cfg(all(feature = "model", feature = "utils"))]
-use utils::{self as serenity_utils, VecMap};
+use utils as serenity_utils;
/// Represents a guild's text or voice channel. Some methods are available only
/// for voice channels and some are only available for text channels.
@@ -77,6 +75,8 @@ pub struct GuildChannel {
// default to `false`.
#[serde(default)]
pub nsfw: bool,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
@@ -94,7 +94,9 @@ impl GuildChannel {
///
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- pub fn broadcast_typing(&self) -> Result<()> { self.id.broadcast_typing() }
+ pub fn broadcast_typing(&self) -> FutureResult<()> {
+ ftryopt!(self.client).http.broadcast_typing(self.id.0)
+ }
/// Creates an invite leading to the given channel.
///
@@ -106,20 +108,22 @@ 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, f: F) -> FutureResult<RichInvite>
where F: FnOnce(CreateInvite) -> CreateInvite {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
let req = Permissions::CREATE_INVITE;
- if !utils::user_has_perms(self.id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(client.cache.borrow().user_has_perms(self.id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- let map = serenity_utils::vecmap_to_json_map(f(CreateInvite::default()).0);
-
- http::create_invite(self.id.0, &map)
+ client.http.create_invite(self.id.0, f)
}
/// Creates a [permission overwrite][`PermissionOverwrite`] for either a
@@ -226,22 +230,27 @@ impl GuildChannel {
/// [Send Messages]: permissions/constant.SEND_MESSAGES.html
/// [Send TTS Messages]: permissions/constant.SEND_TTS_MESSAGES.html
#[inline]
- pub fn create_permission(&self, target: &PermissionOverwrite) -> Result<()> {
- self.id.create_permission(target)
+ pub fn create_permission(&self, target: &PermissionOverwrite)
+ -> FutureResult<()> {
+ ftryopt!(self.client).http.create_permission(self.id.0, target)
}
/// Deletes this channel, returning the channel on a successful deletion.
- pub fn delete(&self) -> Result<Channel> {
+ pub fn delete(&self) -> FutureResult<Channel> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_CHANNELS;
- if !utils::user_has_perms(self.id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(client.cache.borrow().user_has_perms(self.id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.id.delete()
+ client.http.delete_channel(self.id.0)
}
/// Deletes all messages by Ids from the given vector in the channel.
@@ -262,8 +271,9 @@ impl GuildChannel {
/// [`ModelError::BulkDeleteAmount`]: ../enum.ModelError.html#variant.BulkDeleteAmount
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
#[inline]
- pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>(&self, message_ids: It) -> Result<()> {
- self.id.delete_messages(message_ids)
+ pub fn delete_messages<T, It>(&self, message_ids: It) -> FutureResult<()>
+ where T: AsRef<MessageId>, It: IntoIterator<Item=T> {
+ ftryopt!(self.client).http.delete_messages(self.id.0, message_ids)
}
/// Deletes all permission overrides in the channel from a member
@@ -273,8 +283,14 @@ impl GuildChannel {
///
/// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
#[inline]
- pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> {
- self.id.delete_permission(permission_type)
+ pub fn delete_permission(&self, permission_type: PermissionOverwriteType)
+ -> FutureResult<()> {
+ let overwrite_id = match permission_type {
+ PermissionOverwriteType::Member(id) => id.0,
+ PermissionOverwriteType::Role(id) => id.0,
+ };
+
+ ftryopt!(self.client).http.delete_permission(self.id.0, overwrite_id)
}
/// Deletes the given [`Reaction`] from the channel.
@@ -285,13 +301,18 @@ impl GuildChannel {
/// [`Reaction`]: struct.Reaction.html
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
#[inline]
- pub fn delete_reaction<M, R>(&self,
- message_id: M,
- user_id: Option<UserId>,
- reaction_type: R)
- -> Result<()>
- where M: Into<MessageId>, R: Into<ReactionType> {
- self.id.delete_reaction(message_id, user_id, reaction_type)
+ pub fn delete_reaction<M, R>(
+ &self,
+ message_id: M,
+ user_id: Option<UserId>,
+ reaction_type: R,
+ ) -> FutureResult<()> where M: Into<MessageId>, R: Into<ReactionType> {
+ ftryopt!(self.client).http.delete_reaction(
+ self.id.0,
+ message_id.into().0,
+ user_id.map(|x| x.0),
+ &reaction_type.into(),
+ )
}
/// Modifies a channel's settings, such as its position or name.
@@ -306,32 +327,22 @@ impl GuildChannel {
/// channel.edit(|c| c.name("test").bitrate(86400));
/// ```
#[cfg(feature = "utils")]
- pub fn edit<F>(&mut self, f: F) -> Result<()>
- where F: FnOnce(EditChannel) -> EditChannel {
+ pub fn edit<F: FnOnce(EditChannel) -> EditChannel>(&self, f: F)
+ -> FutureResult<GuildChannel> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_CHANNELS;
- if !utils::user_has_perms(self.id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(client.cache.borrow().user_has_perms(self.id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- let mut map = VecMap::new();
- map.insert("name", Value::String(self.name.clone()));
- map.insert("position", Value::Number(Number::from(self.position)));
- map.insert("type", Value::String(self.kind.name().to_string()));
-
- let edited = serenity_utils::vecmap_to_json_map(f(EditChannel(map)).0);
-
- match http::edit_channel(self.id.0, &edited) {
- Ok(channel) => {
- mem::replace(self, channel);
-
- Ok(())
- },
- Err(why) => Err(why),
- }
+ client.http.edit_channel(self.id.0, f)
}
/// Edits a [`Message`] in the channel given its Id.
@@ -354,9 +365,14 @@ impl GuildChannel {
/// [`Message`]: struct.Message.html
/// [`the limit`]: ../builder/struct.EditMessage.html#method.content
#[inline]
- pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
+ pub fn edit_message<F, M>(&self, message_id: M, f: F)
+ -> FutureResult<Message>
where F: FnOnce(EditMessage) -> EditMessage, M: Into<MessageId> {
- self.id.edit_message(message_id, f)
+ ftryopt!(self.client).http.edit_message(
+ self.id.0,
+ message_id.into().0,
+ f,
+ )
}
/// Attempts to find this channel's guild in the Cache.
@@ -364,14 +380,20 @@ 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) -> Option<Rc<RefCell<Guild>>> {
+ self.client.as_ref()?.cache.try_borrow().ok().and_then(|cache| {
+ cache.guild(self.guild_id)
+ })
+ }
/// Gets all of the channel's invites.
///
/// Requires the [Manage Channels] permission.
/// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
#[inline]
- pub fn invites(&self) -> Result<Vec<RichInvite>> { self.id.invites() }
+ pub fn invites(&self) -> FutureResult<Vec<RichInvite>> {
+ ftryopt!(self.client).http.get_channel_invites(self.id.0)
+ }
/// Determines if the channel is NSFW.
///
@@ -395,8 +417,9 @@ impl GuildChannel {
///
/// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
#[inline]
- pub fn message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> {
- self.id.message(message_id)
+ pub fn message<M: Into<MessageId>>(&self, message_id: M)
+ -> FutureResult<Message> {
+ ftryopt!(self.client).http.get_message(self.id.0, message_id.into().0)
}
/// Gets messages from the channel.
@@ -408,9 +431,9 @@ impl GuildChannel {
/// [`Channel::messages`]: enum.Channel.html#method.messages
/// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
#[inline]
- pub fn messages<F>(&self, f: F) -> Result<Vec<Message>>
- where F: FnOnce(GetMessages) -> GetMessages {
- self.id.messages(f)
+ pub fn messages<'a, F: FnOnce(GetMessages) -> GetMessages>(&'a self, f: F)
+ -> Box<Future<Item = Vec<Message>, Error = Error> + 'a> {
+ ftryopt!(self.client).http.get_messages(self.id.0, f)
}
/// Returns the name of the guild channel.
@@ -510,19 +533,24 @@ impl GuildChannel {
/// [Attach Files]: permissions/constant.ATTACH_FILES.html
/// [Send Messages]: permissions/constant.SEND_MESSAGES.html
#[cfg(feature = "cache")]
- pub fn permissions_for<U: Into<UserId>>(&self, user_id: U) -> Result<Permissions> {
+ pub fn permissions_for<U: Into<UserId>>(&self, user_id: U)
+ -> Result<Permissions> {
self.guild()
.ok_or_else(|| Error::Model(ModelError::GuildNotFound))
- .map(|g| g.read().permissions_in(self.id, user_id))
+ .map(|g| g.borrow().permissions_in(self.id, user_id))
}
/// Pins a [`Message`] to the channel.
#[inline]
- pub fn pin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { self.id.pin(message_id) }
+ pub fn pin<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<()> {
+ ftryopt!(self.client).http.pin_message(self.id.0, message_id.into().0)
+ }
/// Gets all channel's pins.
#[inline]
- pub fn pins(&self) -> Result<Vec<Message>> { self.id.pins() }
+ pub fn pins(&self) -> FutureResult<Vec<Message>> {
+ ftryopt!(self.client).http.get_pins(self.id.0)
+ }
/// Gets the list of [`User`]s who have reacted to a [`Message`] with a
/// certain [`Emoji`].
@@ -542,10 +570,15 @@ impl GuildChannel {
reaction_type: R,
limit: Option<u8>,
after: U,
- ) -> Result<Vec<User>> where M: Into<MessageId>,
- R: Into<ReactionType>,
- U: Into<Option<UserId>> {
- self.id.reaction_users(message_id, reaction_type, limit, after)
+ ) -> FutureResult<Vec<User>>
+ where M: Into<MessageId>, R: Into<ReactionType>, U: Into<Option<UserId>> {
+ ftryopt!(self.client).http.get_reaction_users(
+ self.id.0,
+ message_id.into().0,
+ &reaction_type.into(),
+ limit,
+ after.into().map(|x| x.0),
+ )
}
/// Sends a message with just the given message content in the channel.
@@ -559,7 +592,11 @@ impl GuildChannel {
/// [`ChannelId`]: struct.ChannelId.html
/// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
#[inline]
- pub fn say(&self, content: &str) -> Result<Message> { self.id.say(content) }
+ pub fn say(&self, content: &str) -> FutureResult<Message> {
+ ftryopt!(self.client)
+ .http
+ .send_message(self.id.0, |m| m.content(content))
+ }
/// Sends (a) file(s) along with optional message contents.
///
@@ -579,11 +616,15 @@ impl GuildChannel {
/// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
/// [Attach Files]: permissions/constant.ATTACH_FILES.html
/// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- #[inline]
- pub fn send_files<'a, F, T, It: IntoIterator<Item=T>>(&self, files: It, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage, T: Into<AttachmentType<'a>> {
- self.id.send_files(files, f)
- }
+ // todo
+ // #[inline]
+ // pub fn send_files<'a, F, T, It>(&self, files: It, f: F)
+ // -> FutureResult<Message>
+ // where F: FnOnce(CreateMessage) -> CreateMessage,
+ // T: Into<AttachmentType<'a>>,
+ // It: IntoIterator<Item=T> {
+ // ftryopt!(self.client).http.send_files(self.id.0, files, f)
+ // }
/// Sends a message to the channel with the given content.
///
@@ -604,17 +645,22 @@ impl GuildChannel {
/// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
/// [`Message`]: struct.Message.html
/// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) -> Result<Message> {
+ pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F)
+ -> FutureResult<Message> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
let req = Permissions::SEND_MESSAGES;
- if !utils::user_has_perms(self.id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(client.cache.borrow().user_has_perms(self.id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.id.send_message(f)
+ client.http.send_message(self.id.0, f)
}
/// Unpins a [`Message`] in the channel given by its Id.
@@ -624,8 +670,8 @@ impl GuildChannel {
/// [`Message`]: struct.Message.html
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
#[inline]
- pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
- self.id.unpin(message_id)
+ pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<()> {
+ ftryopt!(self.client).http.unpin_message(self.id.0, message_id.into().0)
}
/// Retrieves the channel's webhooks.
@@ -634,7 +680,9 @@ impl GuildChannel {
///
/// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
#[inline]
- pub fn webhooks(&self) -> Result<Vec<Webhook>> { self.id.webhooks() }
+ pub fn webhooks(&self) -> FutureResult<Vec<Webhook>> {
+ ftryopt!(self.client).http.get_channel_webhooks(self.id.0)
+ }
}
#[cfg(feature = "model")]
diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs
index feff845..97c0ab2 100644
--- a/src/model/channel/message.rs
+++ b/src/model/channel/message.rs
@@ -1,19 +1,20 @@
//! Models relating to Discord channels.
use chrono::{DateTime, FixedOffset};
+use futures::future;
use model::prelude::*;
use serde_json::Value;
+use std::cell::RefCell;
+use std::rc::Rc;
+use super::super::WrappedClient;
+use ::FutureResult;
#[cfg(feature = "model")]
-use builder::{CreateEmbed, EditMessage};
-#[cfg(all(feature = "cache", feature = "model"))]
-use CACHE;
+use builder::EditMessage;
#[cfg(all(feature = "cache", feature = "model"))]
use std::fmt::Write;
#[cfg(feature = "model")]
-use std::mem;
-#[cfg(feature = "model")]
-use {constants, http, utils as serenity_utils};
+use constants;
/// A representation of a message over a guild's text channel, a group, or a
/// private channel.
@@ -65,6 +66,8 @@ pub struct Message {
pub tts: bool,
/// The Id of the webhook that sent this message, if one did.
pub webhook_id: Option<WebhookId>,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
@@ -111,13 +114,20 @@ impl Message {
/// # }
/// ```
#[cfg(feature = "cache")]
- #[inline]
- pub fn channel(&self) -> Option<Channel> { CACHE.read().channel(self.channel_id) }
+ pub fn channel(&self) -> Option<Channel> {
+ self.client.as_ref()?.cache.try_borrow().ok()?.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) -> Result<bool> {
+ let client = self.client.as_ref().ok_or_else(|| {
+ Error::Model(ModelError::ClientNotPresent)
+ })?;
+
+ Ok(self.author.id == client.cache.try_borrow()?.user.id)
+ }
/// Deletes the message.
///
@@ -133,19 +143,24 @@ impl Message {
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [`ModelError::InvalidUser`]: enum.ModelError.html#variant.InvalidUser
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- pub fn delete(&self) -> Result<()> {
+ pub fn delete(&self) -> FutureResult<()> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
+ let cache = ftry!(client.cache.try_borrow());
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 == cache.user.id;
+ let has_perms = ftry!(cache.user_has_perms(self.channel_id, req));
if !is_author && !has_perms {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.channel_id.delete_message(self.id)
+ client.http.delete_message(self.channel_id.0, self.id.0)
}
/// Deletes all of the [`Reaction`]s associated with the message.
@@ -161,17 +176,24 @@ impl Message {
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [`Reaction`]: struct.Reaction.html
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- pub fn delete_reactions(&self) -> Result<()> {
+ pub fn delete_reactions(&self) -> FutureResult<()> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_MESSAGES;
- if !utils::user_has_perms(self.channel_id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(client.cache.borrow().user_has_perms(self.channel_id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- http::delete_message_reactions(self.channel_id.0, self.id.0)
+ client.http.delete_message_reactions(
+ self.channel_id.0,
+ self.id.0,
+ )
}
/// Edits this message, replacing the original content with new content.
@@ -206,35 +228,18 @@ impl Message {
/// [`ModelError::MessageTooLong`]: enum.ModelError.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<()>
- where F: FnOnce(EditMessage) -> EditMessage {
+ pub fn edit<F: FnOnce(EditMessage) -> EditMessage>(&self, f: F)
+ -> FutureResult<Message> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
- if self.author.id != CACHE.read().user.id {
- return Err(Error::Model(ModelError::InvalidUser));
+ if self.author.id != ftry!(client.cache.try_borrow()).user.id {
+ return Box::new(future::err(Error::Model(ModelError::InvalidUser)));
}
}
- let mut builder = EditMessage::default();
-
- if !self.content.is_empty() {
- builder = builder.content(&self.content);
- }
-
- if let Some(embed) = self.embeds.get(0) {
- builder = builder.embed(|_| CreateEmbed::from(embed.clone()));
- }
-
- let map = serenity_utils::vecmap_to_json_map(f(builder).0);
-
- match http::edit_message(self.channel_id.0, self.id.0, &Value::Object(map)) {
- Ok(edited) => {
- mem::replace(self, edited);
-
- Ok(())
- },
- Err(why) => Err(why),
- }
+ ftryopt!(self.client).http.edit_message(self.channel_id.0, self.id.0, f)
}
pub(crate) fn transform_content(&mut self) {
@@ -275,14 +280,21 @@ impl Message {
result = result.replace(&u.mention(), &at_distinct);
}
- // Then replace all role mentions.
- for id in &self.mention_roles {
- let mention = id.mention();
-
- if let Some(role) = id.find() {
- result = result.replace(&mention, &format!("@{}", role.name));
- } else {
- result = result.replace(&mention, "@deleted-role");
+ if let Some(guild) = self.guild() {
+ if let Some(guild) = guild.try_borrow().ok() {
+ // Then replace all role mentions.
+ for id in &self.mention_roles {
+ let mention = id.mention();
+
+ if let Some(role) = guild.roles.get(id).and_then(|r| r.try_borrow().ok()) {
+ result = result.replace(
+ &mention,
+ &format!("@{}", role.name),
+ );
+ } else {
+ result = result.replace(&mention, "@deleted-role");
+ }
+ }
}
}
@@ -314,9 +326,15 @@ impl Message {
reaction_type: R,
limit: Option<u8>,
after: U,
- ) -> Result<Vec<User>> where R: Into<ReactionType>,
- U: Into<Option<UserId>> {
- self.channel_id.reaction_users(self.id, reaction_type, limit, after)
+ ) -> FutureResult<Vec<User>>
+ where R: Into<ReactionType>, U: Into<Option<UserId>> {
+ ftryopt!(self.client).http.get_reaction_users(
+ self.channel_id.0,
+ self.id.0,
+ &reaction_type.into(),
+ limit,
+ after.into().map(|x| x.0),
+ )
}
/// Returns the associated `Guild` for the message if one is in the cache.
@@ -328,9 +346,10 @@ impl Message {
///
/// [`guild_id`]: #method.guild_id
#[cfg(feature = "cache")]
- pub fn guild(&self) -> Option<Arc<RwLock<Guild>>> {
- self.guild_id()
- .and_then(|guild_id| CACHE.read().guild(guild_id))
+ pub fn guild(&self) -> Option<Rc<RefCell<Guild>>> {
+ self.guild_id().and_then(|guild_id| {
+ self.client.as_ref()?.cache.try_borrow().ok()?.guild(guild_id)
+ })
}
/// Retrieves the Id of the guild that the message was sent in, if sent in
@@ -340,19 +359,25 @@ impl Message {
/// cache.
#[cfg(feature = "cache")]
pub fn guild_id(&self) -> Option<GuildId> {
- match CACHE.read().channel(self.channel_id) {
- Some(Channel::Guild(ch)) => Some(ch.read().guild_id),
+ match self.client.as_ref()?.cache.try_borrow().ok()?.channel(self.channel_id) {
+ Some(Channel::Guild(ch)) => Some(ch.borrow().guild_id),
_ => None,
}
}
/// True if message was sent using direct messages.
+ ///
+ /// Returns `false` if a client is not present on the model.
#[cfg(feature = "cache")]
pub fn is_private(&self) -> bool {
- match CACHE.read().channel(self.channel_id) {
- Some(Channel::Group(_)) | Some(Channel::Private(_)) => true,
- _ => false,
- }
+ self.client.as_ref().map(|client| {
+ let cache = client.cache.borrow();
+
+ match cache.channel(self.channel_id) {
+ Some(Channel::Group(_)) | Some(Channel::Private(_)) => true,
+ _ => false,
+ }
+ }).unwrap_or(false)
}
/// Retrieves a clone of the author's Member instance, if this message was
@@ -364,8 +389,10 @@ 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) -> Option<Rc<RefCell<Member>>> {
+ self.guild().and_then(|guild| {
+ guild.try_borrow().ok()?.members.get(&self.author.id).cloned()
+ })
}
/// Checks the length of a string to ensure that it is within Discord's
@@ -399,17 +426,21 @@ impl Message {
///
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- pub fn pin(&self) -> Result<()> {
+ pub fn pin(&self) -> FutureResult<()> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_MESSAGES;
- if !utils::user_has_perms(self.channel_id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(client.cache.borrow().user_has_perms(self.channel_id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.channel_id.pin(self.id.0)
+ client.http.pin_message(self.channel_id.0, self.id.0)
}
/// React to the message with a custom [`Emoji`] or unicode character.
@@ -426,17 +457,26 @@ impl Message {
/// [`Emoji`]: struct.Emoji.html
/// [Add Reactions]: permissions/constant.ADD_REACTIONS.html
/// [permissions]: permissions
- pub fn react<R: Into<ReactionType>>(&self, reaction_type: R) -> Result<()> {
+ pub fn react<R: Into<ReactionType>>(&self, reaction_type: R)
+ -> FutureResult<()> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
let req = Permissions::ADD_REACTIONS;
- if !utils::user_has_perms(self.channel_id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(client.cache.borrow().user_has_perms(self.channel_id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- http::create_reaction(self.channel_id.0, self.id.0, &reaction_type.into())
+ client.http.create_reaction(
+ self.channel_id.0,
+ self.id.0,
+ &reaction_type.into(),
+ )
}
/// Replies to the user, mentioning them prior to the content in the form
@@ -461,17 +501,23 @@ impl Message {
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
/// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- pub fn reply(&self, content: &str) -> Result<Message> {
+ pub fn reply(&self, content: &str) -> FutureResult<Message> {
if let Some(length_over) = Message::overflow_length(content) {
- return Err(Error::Model(ModelError::MessageTooLong(length_over)));
+ return Box::new(future::err(Error::Model(
+ ModelError::MessageTooLong(length_over),
+ )));
}
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
let req = Permissions::SEND_MESSAGES;
- if !utils::user_has_perms(self.channel_id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(client.cache.borrow().user_has_perms(self.channel_id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
@@ -479,12 +525,12 @@ impl Message {
gen.push_str(": ");
gen.push_str(content);
- let map = json!({
- "content": gen,
- "tts": false,
- });
+ let done = client.http.send_message(
+ self.channel_id.0,
+ |f| f.content(gen).tts(false),
+ );
- http::send_message(self.channel_id.0, &map)
+ Box::new(done)
}
/// Unpins the message from its channel.
@@ -499,17 +545,21 @@ impl Message {
///
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- pub fn unpin(&self) -> Result<()> {
+ pub fn unpin(&self) -> FutureResult<()> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_MESSAGES;
- if !utils::user_has_perms(self.channel_id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(client.cache.borrow().user_has_perms(self.channel_id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- http::unpin_message(self.channel_id.0, self.id.0)
+ client.http.unpin_message(self.channel_id.0, self.id.0)
}
pub(crate) fn check_content_length(map: &JsonMap) -> Result<()> {
diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs
index eadc7bb..9177064 100644
--- a/src/model/channel/mod.rs
+++ b/src/model/channel/mod.rs
@@ -20,17 +20,18 @@ pub use self::private_channel::*;
pub use self::reaction::*;
pub use self::channel_category::*;
-use internal::RwLockExt;
+use futures::Future;
use model::prelude::*;
use serde::de::Error as DeError;
use serde::ser::{SerializeStruct, Serialize, Serializer};
use serde_json;
+use std::cell::RefCell;
+use std::rc::Rc;
use super::utils::deserialize_u64;
+use ::FutureResult;
#[cfg(feature = "model")]
-use builder::{CreateMessage, EditMessage, GetMessages};
-#[cfg(feature = "model")]
-use http::AttachmentType;
+use builder::{CreateMessage, EditMessage};
#[cfg(feature = "model")]
use std::fmt::{Display, Formatter, Result as FmtResult};
@@ -38,30 +39,25 @@ use std::fmt::{Display, Formatter, Result as FmtResult};
#[derive(Clone, Debug)]
pub enum Channel {
/// A group. A group comprises of only one channel.
- Group(Arc<RwLock<Group>>),
+ Group(Rc<RefCell<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(Arc<RwLock<GuildChannel>>),
+ Guild(Rc<RefCell<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(Arc<RwLock<PrivateChannel>>),
+ Private(Rc<RefCell<PrivateChannel>>),
/// A category of [`GuildChannel`]s
///
/// [`GuildChannel`]: struct.GuildChannel.html
- Category(Arc<RwLock<ChannelCategory>>),
+ Category(Rc<RefCell<ChannelCategory>>),
}
impl Channel {
-
- /////////////////////////////////////////////////////////////////////////
- // Adapter for each variant
- /////////////////////////////////////////////////////////////////////////
-
/// Converts from `Channel` to `Option<Arc<RwLock<Group>>>`.
///
/// Converts `self` into an `Option<Arc<RwLock<Group>>>`, consuming `self`,
@@ -88,9 +84,7 @@ impl Channel {
/// }
/// # }
/// ```
-
-
- pub fn group(self) -> Option<Arc<RwLock<Group>>> {
+ pub fn group(self) -> Option<Rc<RefCell<Group>>> {
match self {
Channel::Group(lock) => Some(lock),
_ => None,
@@ -119,8 +113,7 @@ impl Channel {
/// }
/// # }
/// ```
-
- pub fn guild(self) -> Option<Arc<RwLock<GuildChannel>>> {
+ pub fn guild(self) -> Option<Rc<RefCell<GuildChannel>>> {
match self {
Channel::Guild(lock) => Some(lock),
_ => None,
@@ -152,8 +145,7 @@ impl Channel {
/// }
/// # }
/// ```
-
- pub fn private(self) -> Option<Arc<RwLock<PrivateChannel>>> {
+ pub fn private(self) -> Option<Rc<RefCell<PrivateChannel>>> {
match self {
Channel::Private(lock) => Some(lock),
_ => None,
@@ -182,8 +174,7 @@ impl Channel {
/// }
/// # }
/// ```
-
- pub fn category(self) -> Option<Arc<RwLock<ChannelCategory>>> {
+ pub fn category(self) -> Option<Rc<RefCell<ChannelCategory>>> {
match self {
Channel::Category(lock) => Some(lock),
_ => None,
@@ -205,9 +196,13 @@ impl Channel {
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
- pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R) -> Result<()>
- where M: Into<MessageId>, R: Into<ReactionType> {
- self.id().create_reaction(message_id, reaction_type)
+ pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R)
+ -> FutureResult<()> where M: Into<MessageId>, R: Into<ReactionType> {
+ ftryopt!(ftry!(self.client())).http.create_reaction(
+ self.id().0,
+ message_id.into().0,
+ &reaction_type.into(),
+ )
}
/// Deletes the inner channel.
@@ -217,23 +212,21 @@ impl Channel {
///
/// [`Group`]: struct.Group.html
#[cfg(feature = "model")]
- pub fn delete(&self) -> Result<()> {
+ pub fn delete(&self) -> FutureResult<()> {
match *self {
Channel::Group(ref group) => {
- let _ = group.read().leave()?;
+ Box::new(group.borrow().leave())
},
Channel::Guild(ref public_channel) => {
- let _ = public_channel.read().delete()?;
+ Box::new(public_channel.borrow().delete().map(|_| ()))
},
Channel::Private(ref private_channel) => {
- let _ = private_channel.read().delete()?;
+ Box::new(private_channel.borrow().delete().map(|_| ()))
},
Channel::Category(ref category) => {
- category.read().delete()?;
+ Box::new(category.borrow().delete())
},
}
-
- Ok(())
}
/// Deletes a [`Message`] given its Id.
@@ -249,8 +242,12 @@ impl Channel {
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
- pub fn delete_message<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
- self.id().delete_message(message_id)
+ pub fn delete_message<M>(&self, message_id: M) -> FutureResult<()>
+ where M: Into<MessageId> {
+ ftryopt!(ftry!(self.client())).http.delete_message(
+ self.id().0,
+ message_id.into().0,
+ )
}
/// Deletes the given [`Reaction`] from the channel.
@@ -263,14 +260,18 @@ impl Channel {
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
- pub fn delete_reaction<M, R>(&self,
- message_id: M,
- user_id: Option<UserId>,
- reaction_type: R)
- -> Result<()>
- where M: Into<MessageId>, R: Into<ReactionType> {
- self.id()
- .delete_reaction(message_id, user_id, reaction_type)
+ pub fn delete_reaction<M, R>(
+ &self,
+ message_id: M,
+ user_id: Option<UserId>,
+ reaction_type: R,
+ ) -> FutureResult<()> where M: Into<MessageId>, R: Into<ReactionType> {
+ ftryopt!(ftry!(self.client())).http.delete_reaction(
+ self.id().0,
+ message_id.into().0,
+ user_id.map(|x| x.0),
+ &reaction_type.into(),
+ )
}
/// Edits a [`Message`] in the channel given its Id.
@@ -295,9 +296,14 @@ impl Channel {
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
- pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
+ pub fn edit_message<F, M>(&self, message_id: M, f: F)
+ -> FutureResult<Message>
where F: FnOnce(EditMessage) -> EditMessage, M: Into<MessageId> {
- self.id().edit_message(message_id, f)
+ ftryopt!(ftry!(self.client())).http.edit_message(
+ self.id().0,
+ message_id.into().0,
+ f,
+ )
}
/// Determines if the channel is NSFW.
@@ -309,8 +315,8 @@ impl Channel {
#[inline]
pub fn is_nsfw(&self) -> bool {
match *self {
- Channel::Guild(ref channel) => channel.with(|c| c.is_nsfw()),
- Channel::Category(ref category) => category.with(|c| c.is_nsfw()),
+ Channel::Guild(ref channel) => channel.borrow().is_nsfw(),
+ Channel::Category(ref category) => category.borrow().is_nsfw(),
Channel::Group(_) | Channel::Private(_) => false,
}
}
@@ -323,32 +329,12 @@ impl Channel {
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
- pub fn message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> {
- self.id().message(message_id)
- }
-
- /// Gets messages from the channel.
- ///
- /// Requires the [Read Message History] permission.
- ///
- /// # Examples
- ///
- /// ```rust,ignore
- /// use serenity::model::MessageId;
- ///
- /// let id = MessageId(81392407232380928);
- ///
- /// // Maximum is 100.
- /// let _messages = channel.messages(|g| g.after(id).limit(100));
- /// ```
- ///
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
- #[cfg(feature = "model")]
- #[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
- #[inline]
- pub fn messages<F>(&self, f: F) -> Result<Vec<Message>>
- where F: FnOnce(GetMessages) -> GetMessages {
- self.id().messages(f)
+ pub fn message<M>(&self, message_id: M) -> FutureResult<Message>
+ where M: Into<MessageId> {
+ ftryopt!(ftry!(self.client())).http.get_message(
+ self.id().0,
+ message_id.into().0,
+ )
}
/// Gets the list of [`User`]s who have reacted to a [`Message`] with a
@@ -375,10 +361,15 @@ impl Channel {
reaction_type: R,
limit: Option<u8>,
after: U,
- ) -> Result<Vec<User>> where M: Into<MessageId>,
- R: Into<ReactionType>,
- U: Into<Option<UserId>> {
- self.id().reaction_users(message_id, reaction_type, limit, after)
+ ) -> FutureResult<Vec<User>>
+ where M: Into<MessageId>, R: Into<ReactionType>, U: Into<Option<UserId>> {
+ ftryopt!(ftry!(self.client())).http.get_reaction_users(
+ self.id().0,
+ message_id.into().0,
+ &reaction_type.into(),
+ limit,
+ after.into().map(|x| x.0),
+ )
}
/// Retrieves the Id of the inner [`Group`], [`GuildChannel`], or
@@ -389,10 +380,10 @@ impl Channel {
/// [`PrivateChannel`]: struct.PrivateChannel.html
pub fn id(&self) -> ChannelId {
match *self {
- Channel::Group(ref group) => group.with(|g| g.channel_id),
- Channel::Guild(ref ch) => ch.with(|c| c.id),
- Channel::Private(ref ch) => ch.with(|c| c.id),
- Channel::Category(ref category) => category.with(|c| c.id),
+ Channel::Group(ref group) => group.borrow().channel_id,
+ Channel::Guild(ref ch) => ch.borrow().id,
+ Channel::Private(ref ch) => ch.borrow().id,
+ Channel::Category(ref category) => category.borrow().id,
}
}
@@ -409,7 +400,10 @@ impl Channel {
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
- pub fn say(&self, content: &str) -> Result<Message> { self.id().say(content) }
+ pub fn say(&self, content: &str) -> FutureResult<Message> {
+ ftryopt!(ftry!(self.client())).http.send_message(self.id().0, |f|
+ f.content(content))
+ }
/// Sends (a) file(s) along with optional message contents.
///
@@ -429,13 +423,17 @@ impl Channel {
/// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
/// [Attach Files]: permissions/constant.ATTACH_FILES.html
/// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- #[cfg(feature = "model")]
- #[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
- #[inline]
- pub fn send_files<'a, F, T, It: IntoIterator<Item=T>>(&self, files: It, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage, T: Into<AttachmentType<'a>> {
- self.id().send_files(files, f)
- }
+ // todo
+ // #[cfg(feature = "model")]
+ // #[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
+ // #[inline]
+ // pub fn send_files<'a, F, T, It>(&self, files: It, f: F)
+ // -> FutureResult<Message>
+ // where F: FnOnce(CreateMessage) -> CreateMessage,
+ // T: Into<AttachmentType<'a>>,
+ // It: IntoIterator<Item = T> {
+ // ftryopt!(ftry!(self.client())).http.send_files(self.id(), files, f)
+ // }
/// Sends a message to the channel.
///
@@ -459,9 +457,9 @@ impl Channel {
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
- pub fn send_message<F>(&self, f: F) -> Result<Message>
+ pub fn send_message<F>(&self, f: F) -> FutureResult<Message>
where F: FnOnce(CreateMessage) -> CreateMessage {
- self.id().send_message(f)
+ ftryopt!(ftry!(self.client())).http.send_message(self.id().0, f)
}
/// Unpins a [`Message`] in the channel given by its Id.
@@ -473,8 +471,20 @@ impl Channel {
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
- pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
- self.id().unpin(message_id)
+ pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<()> {
+ let retrieve = ftry!(self.client());
+ let obtained = ftryopt!(retrieve);
+
+ obtained.http.unpin_message(self.id().0, message_id.into().0)
+ }
+
+ fn client(&self) -> Result<WrappedClient> {
+ Ok(match *self {
+ Channel::Category(ref c) => c.try_borrow()?.client.as_ref().map(Rc::clone),
+ Channel::Group(ref c) => c.try_borrow()?.client.as_ref().map(Rc::clone),
+ Channel::Guild(ref c) => c.try_borrow()?.client.as_ref().map(Rc::clone),
+ Channel::Private(ref c) => c.try_borrow()?.client.as_ref().map(Rc::clone),
+ })
}
}
@@ -489,16 +499,16 @@ impl<'de> Deserialize<'de> for Channel {
match kind {
0 | 2 => serde_json::from_value::<GuildChannel>(Value::Object(v))
- .map(|x| Channel::Guild(Arc::new(RwLock::new(x))))
+ .map(|x| Channel::Guild(Rc::new(RefCell::new(x))))
.map_err(DeError::custom),
1 => serde_json::from_value::<PrivateChannel>(Value::Object(v))
- .map(|x| Channel::Private(Arc::new(RwLock::new(x))))
+ .map(|x| Channel::Private(Rc::new(RefCell::new(x))))
.map_err(DeError::custom),
3 => serde_json::from_value::<Group>(Value::Object(v))
- .map(|x| Channel::Group(Arc::new(RwLock::new(x))))
+ .map(|x| Channel::Group(Rc::new(RefCell::new(x))))
.map_err(DeError::custom),
4 => serde_json::from_value::<ChannelCategory>(Value::Object(v))
- .map(|x| Channel::Category(Arc::new(RwLock::new(x))))
+ .map(|x| Channel::Category(Rc::new(RefCell::new(x))))
.map_err(DeError::custom),
_ => Err(DeError::custom("Unknown channel type")),
}
@@ -510,16 +520,16 @@ impl Serialize for Channel {
where S: Serializer {
match *self {
Channel::Category(ref c) => {
- ChannelCategory::serialize(&*c.read(), serializer)
+ ChannelCategory::serialize(&*c.borrow(), serializer)
},
Channel::Group(ref c) => {
- Group::serialize(&*c.read(), serializer)
+ Group::serialize(&*c.borrow(), serializer)
},
Channel::Guild(ref c) => {
- GuildChannel::serialize(&*c.read(), serializer)
+ GuildChannel::serialize(&*c.borrow(), serializer)
},
Channel::Private(ref c) => {
- PrivateChannel::serialize(&*c.read(), serializer)
+ PrivateChannel::serialize(&*c.borrow(), serializer)
},
}
}
@@ -542,15 +552,15 @@ impl Display for Channel {
/// [`PrivateChannel`]: struct.PrivateChannel.html
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match *self {
- Channel::Group(ref group) => Display::fmt(&group.read().name(), f),
- Channel::Guild(ref ch) => Display::fmt(&ch.read().id.mention(), f),
+ Channel::Group(ref group) => Display::fmt(&group.borrow().name(), f),
+ Channel::Guild(ref ch) => Display::fmt(&ch.borrow().id.mention(), f),
Channel::Private(ref ch) => {
- let channel = ch.read();
- let recipient = channel.recipient.read();
+ let channel = ch.borrow();
+ let recipient = channel.recipient.borrow();
Display::fmt(&recipient.name, f)
},
- Channel::Category(ref category) => Display::fmt(&category.read().name, f),
+ Channel::Category(ref category) => Display::fmt(&category.borrow().name, f),
}
}
}
diff --git a/src/model/channel/private_channel.rs b/src/model/channel/private_channel.rs
index 7c8b1e8..48d887b 100644
--- a/src/model/channel/private_channel.rs
+++ b/src/model/channel/private_channel.rs
@@ -1,14 +1,15 @@
use chrono::{DateTime, FixedOffset};
+use futures::Future;
use model::prelude::*;
+use std::cell::RefCell;
use std::fmt::{Display, Formatter, Result as FmtResult};
+use std::rc::Rc;
+use super::super::WrappedClient;
use super::deserialize_single_recipient;
+use ::FutureResult;
#[cfg(feature = "model")]
use builder::{CreateMessage, EditMessage, GetMessages};
-#[cfg(feature = "model")]
-use http::AttachmentType;
-#[cfg(feature = "model")]
-use internal::RwLockExt;
/// A Direct Message text channel with another user.
#[derive(Clone, Debug, Deserialize, Serialize)]
@@ -33,14 +34,18 @@ pub struct PrivateChannel {
/// The recipient to the private channel.
#[serde(deserialize_with = "deserialize_single_recipient",
rename = "recipients",
- serialize_with = "serialize_sync_user")]
- pub recipient: Arc<RwLock<User>>,
+ serialize_with = "serialize_user")]
+ pub recipient: Rc<RefCell<User>>,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
impl PrivateChannel {
/// Broadcasts that the current user is typing to the recipient.
- pub fn broadcast_typing(&self) -> Result<()> { self.id.broadcast_typing() }
+ pub fn broadcast_typing(&self) -> FutureResult<()> {
+ ftryopt!(self.client).http.broadcast_typing(self.id.0)
+ }
/// React to a [`Message`] with a custom [`Emoji`] or unicode character.
///
@@ -54,16 +59,22 @@ impl PrivateChannel {
/// [`Message`]: struct.Message.html
/// [`Message::react`]: struct.Message.html#method.react
/// [Add Reactions]: permissions/constant.ADD_REACTIONS.html
- pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R) -> Result<()>
- where M: Into<MessageId>, R: Into<ReactionType> {
- self.id.create_reaction(message_id, reaction_type)
+ pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R)
+ -> FutureResult<()> where M: Into<MessageId>, R: Into<ReactionType> {
+ ftryopt!(self.client).http.create_reaction(
+ self.id.0,
+ message_id.into().0,
+ &reaction_type.into(),
+ )
}
/// Deletes the channel. This does not delete the contents of the channel,
/// and is equivalent to closing a private channel on the client, which can
/// be re-opened.
#[inline]
- pub fn delete(&self) -> Result<Channel> { self.id.delete() }
+ pub fn delete(&self) -> FutureResult<Channel> {
+ ftryopt!(self.client).http.delete_channel(self.id.0)
+ }
/// Deletes all messages by Ids from the given vector in the channel.
///
@@ -83,8 +94,11 @@ impl PrivateChannel {
/// [`ModelError::BulkDeleteAmount`]: ../enum.ModelError.html#variant.BulkDeleteAmount
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
#[inline]
- pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>(&self, message_ids: It) -> Result<()> {
- self.id.delete_messages(message_ids)
+ pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>(
+ &self,
+ message_ids: It,
+ ) -> FutureResult<()> {
+ ftryopt!(self.client).http.delete_messages(self.id.0, message_ids)
}
/// Deletes all permission overrides in the channel from a member
@@ -94,8 +108,14 @@ impl PrivateChannel {
///
/// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
#[inline]
- pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> {
- self.id.delete_permission(permission_type)
+ pub fn delete_permission(&self, permission_type: PermissionOverwriteType)
+ -> FutureResult<()> {
+ let overwrite_id = match permission_type {
+ PermissionOverwriteType::Member(id) => id.0,
+ PermissionOverwriteType::Role(id) => id.0,
+ };
+
+ ftryopt!(self.client).http.delete_permission(self.id.0, overwrite_id)
}
/// Deletes the given [`Reaction`] from the channel.
@@ -106,13 +126,18 @@ impl PrivateChannel {
/// [`Reaction`]: struct.Reaction.html
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
#[inline]
- pub fn delete_reaction<M, R>(&self,
- message_id: M,
- user_id: Option<UserId>,
- reaction_type: R)
- -> Result<()>
- where M: Into<MessageId>, R: Into<ReactionType> {
- self.id.delete_reaction(message_id, user_id, reaction_type)
+ pub fn delete_reaction<M: Into<MessageId>, R: Into<ReactionType>>(
+ &self,
+ message_id: M,
+ user_id: Option<UserId>,
+ reaction_type: R,
+ ) -> FutureResult<()> {
+ ftryopt!(self.client).http.delete_reaction(
+ self.id.0,
+ message_id.into().0,
+ user_id.map(|x| x.0),
+ &reaction_type.into(),
+ )
}
/// Edits a [`Message`] in the channel given its Id.
@@ -135,9 +160,14 @@ impl PrivateChannel {
/// [`Message`]: struct.Message.html
/// [`the limit`]: ../builder/struct.EditMessage.html#method.content
#[inline]
- pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
+ pub fn edit_message<F, M>(&self, message_id: M, f: F)
+ -> FutureResult<Message>
where F: FnOnce(EditMessage) -> EditMessage, M: Into<MessageId> {
- self.id.edit_message(message_id, f)
+ ftryopt!(self.client).http.edit_message(
+ self.id.0,
+ message_id.into().0,
+ f,
+ )
}
/// Determines if the channel is NSFW.
@@ -157,8 +187,9 @@ impl PrivateChannel {
///
/// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
#[inline]
- pub fn message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> {
- self.id.message(message_id)
+ pub fn message<M: Into<MessageId>>(&self, message_id: M)
+ -> FutureResult<Message> {
+ ftryopt!(self.client).http.get_message(self.id.0, message_id.into().0)
}
/// Gets messages from the channel.
@@ -170,13 +201,15 @@ impl PrivateChannel {
/// [`Channel::messages`]: enum.Channel.html#method.messages
/// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
#[inline]
- pub fn messages<F>(&self, f: F) -> Result<Vec<Message>>
- where F: FnOnce(GetMessages) -> GetMessages {
- self.id.messages(f)
+ pub fn messages<'a, F: FnOnce(GetMessages) -> GetMessages>(&'a self, f: F)
+ -> Box<Future<Item = Vec<Message>, Error = Error> + 'a> {
+ ftryopt!(self.client).http.get_messages(self.id.0, f)
}
/// Returns "DM with $username#discriminator".
- pub fn name(&self) -> String { format!("DM with {}", self.recipient.with(|r| r.tag())) }
+ pub fn name(&self) -> String {
+ format!("DM with {}", self.recipient.borrow().tag())
+ }
/// Gets the list of [`User`]s who have reacted to a [`Message`] with a
/// certain [`Emoji`].
@@ -191,27 +224,37 @@ impl PrivateChannel {
/// [`User`]: struct.User.html
/// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
#[inline]
- pub fn reaction_users<M, R, U>(&self,
+ pub fn reaction_users<M, R, U>(
+ &self,
message_id: M,
reaction_type: R,
limit: Option<u8>,
after: U,
- ) -> Result<Vec<User>> where M: Into<MessageId>,
- R: Into<ReactionType>,
- U: Into<Option<UserId>> {
- self.id.reaction_users(message_id, reaction_type, limit, after)
+ ) -> FutureResult<Vec<User>>
+ where M: Into<MessageId>, R: Into<ReactionType>, U: Into<Option<UserId>> {
+ ftryopt!(self.client).http.get_reaction_users(
+ self.id.0,
+ message_id.into().0,
+ &reaction_type.into(),
+ limit,
+ after.into().map(|x| x.0),
+ )
}
/// Pins a [`Message`] to the channel.
///
/// [`Message`]: struct.Message.html
#[inline]
- pub fn pin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { self.id.pin(message_id) }
+ pub fn pin<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<()> {
+ ftryopt!(self.client).http.pin_message(self.id.0, message_id.into().0)
+ }
/// Retrieves the list of messages that have been pinned in the private
/// channel.
#[inline]
- pub fn pins(&self) -> Result<Vec<Message>> { self.id.pins() }
+ pub fn pins(&self) -> FutureResult<Vec<Message>> {
+ ftryopt!(self.client).http.get_pins(self.id.0)
+ }
/// Sends a message with just the given message content in the channel.
///
@@ -224,7 +267,12 @@ impl PrivateChannel {
/// [`ChannelId`]: ../model/id/struct.ChannelId.html
/// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
#[inline]
- pub fn say<D: ::std::fmt::Display>(&self, content: D) -> Result<Message> { self.id.say(content) }
+ pub fn say<D: ::std::fmt::Display>(&self, content: D)
+ -> FutureResult<Message> {
+ ftryopt!(self.client)
+ .http
+ .send_message(self.id.0, |m| m.content(content))
+ }
/// Sends (a) file(s) along with optional message contents.
///
@@ -244,11 +292,15 @@ impl PrivateChannel {
/// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
/// [Attach Files]: permissions/constant.ATTACH_FILES.html
/// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- #[inline]
- pub fn send_files<'a, F, T, It: IntoIterator<Item=T>>(&self, files: It, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage, T: Into<AttachmentType<'a>> {
- self.id.send_files(files, f)
- }
+ // todo
+ // #[inline]
+ // pub fn send_files<'a, F, T, It>(&self, files: It, f: F)
+ // -> FutureResult<Message>
+ // where F: FnOnce(CreateMessage) -> CreateMessage,
+ // T: Into<AttachmentType<'a>>,
+ // It: IntoIterator<Item=T> {
+ // ftryopt!(self.client).http.send_files(self.id.0, files, f)
+ // }
/// Sends a message to the channel with the given content.
///
@@ -265,8 +317,9 @@ impl PrivateChannel {
/// [`CreateMessage`]: ../builder/struct.CreateMessage.html
/// [`Message`]: struct.Message.html
#[inline]
- pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) -> Result<Message> {
- self.id.send_message(f)
+ pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F)
+ -> FutureResult<Message> {
+ ftryopt!(self.client).http.send_message(self.id.0, f)
}
/// Unpins a [`Message`] in the channel given by its Id.
@@ -276,14 +329,14 @@ impl PrivateChannel {
/// [`Message`]: struct.Message.html
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
#[inline]
- pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
- self.id.unpin(message_id)
+ pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<()> {
+ ftryopt!(self.client).http.unpin_message(self.id.0, message_id.into().0)
}
}
impl Display for PrivateChannel {
/// Formats the private channel, displaying the recipient's username.
fn fmt(&self, f: &mut Formatter) -> FmtResult {
- f.write_str(&self.recipient.read().name)
+ f.write_str(&self.recipient.borrow().name)
}
}
diff --git a/src/model/channel/reaction.rs b/src/model/channel/reaction.rs
index 42c3578..28ddb8d 100644
--- a/src/model/channel/reaction.rs
+++ b/src/model/channel/reaction.rs
@@ -1,16 +1,14 @@
+use futures::future;
use model::prelude::*;
use serde::de::{Deserialize, Error as DeError, MapAccess, Visitor};
use serde::ser::{SerializeMap, Serialize, Serializer};
use std::error::Error as StdError;
use std::fmt::{Display, Formatter, Result as FmtResult, Write as FmtWrite};
use std::str::FromStr;
+use super::super::WrappedClient;
+use ::FutureResult;
use internal::prelude::*;
-#[cfg(all(feature = "cache", feature = "model"))]
-use CACHE;
-#[cfg(feature = "model")]
-use http;
-
/// An emoji reaction to a message.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Reaction {
@@ -29,6 +27,8 @@ pub struct Reaction {
///
/// [`User`]: struct.User.html
pub user_id: UserId,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
@@ -43,8 +43,8 @@ impl Reaction {
///
/// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
#[inline]
- pub fn channel(&self) -> Result<Channel> {
- self.channel_id.get()
+ pub fn channel(&self) -> FutureResult<Channel> {
+ ftryopt!(self.client).http.get_channel(self.channel_id.0)
}
/// Deletes the reaction, but only if the current user is the user who made
@@ -62,36 +62,45 @@ impl Reaction {
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
/// [permissions]: permissions
- pub fn delete(&self) -> Result<()> {
- let user_id = feature_cache! {
- {
- let user = if self.user_id == CACHE.read().user.id {
- None
- } else {
- Some(self.user_id.0)
- };
-
- // If the reaction is one _not_ made by the current user, then ensure
- // that the current user has permission* to delete the reaction.
- //
- // Normally, users can only delete their own reactions.
- //
- // * The `Manage Messages` permission.
- if user.is_some() {
- let req = Permissions::MANAGE_MESSAGES;
-
- if !utils::user_has_perms(self.channel_id, req).unwrap_or(true) {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
- }
- }
+ pub fn delete(&self) -> FutureResult<()> {
+ let client = ftryopt!(self.client);
+
+ let user_id = feature_cache! {{
+ let cache = ftry!(client.cache.try_borrow());
- user
+ let user = if self.user_id == cache.user.id {
+ None
} else {
Some(self.user_id.0)
+ };
+
+ // If the reaction is one _not_ made by the current user, then ensure
+ // that the current user has permission* to delete the reaction.
+ //
+ // Normally, users can only delete their own reactions.
+ //
+ // * The `Manage Messages` permission.
+ if user.is_some() {
+ let req = Permissions::MANAGE_MESSAGES;
+
+ if !cache.user_has_perms(self.channel_id, req).unwrap_or(true) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
+ }
}
- };
- http::delete_reaction(self.channel_id.0, self.message_id.0, user_id, &self.emoji)
+ user
+ } else {
+ Some(self.user_id.0)
+ }};
+
+ ftryopt!(self.client).http.delete_reaction(
+ self.channel_id.0,
+ self.message_id.0,
+ user_id,
+ &self.emoji,
+ )
}
/// Retrieves the [`Message`] associated with this reaction.
@@ -105,8 +114,11 @@ impl Reaction {
/// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
/// [`Message`]: struct.Message.html
#[inline]
- pub fn message(&self) -> Result<Message> {
- self.channel_id.message(self.message_id)
+ pub fn message(&self) -> FutureResult<Message> {
+ ftryopt!(self.client).http.get_message(
+ self.channel_id.0,
+ self.message_id.0,
+ )
}
/// Retrieves the user that made the reaction.
@@ -115,8 +127,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.get()
+ pub fn user(&self) -> FutureResult<User> {
+ ftryopt!(self.client).http.get_user(self.user_id.0)
}
/// Retrieves the list of [`User`]s who have reacted to a [`Message`] with a
@@ -144,17 +156,18 @@ impl Reaction {
/// [`User`]: struct.User.html
/// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
/// [permissions]: permissions
- pub fn users<R, U>(&self,
- reaction_type: R,
- limit: Option<u8>,
- after: Option<U>)
- -> Result<Vec<User>>
+ pub fn users<R, U>(
+ &self,
+ reaction_type: R,
+ limit: Option<u8>,
+ after: Option<U>,
+ ) -> FutureResult<Vec<User>>
where R: Into<ReactionType>, U: Into<UserId> {
- http::get_reaction_users(
+ ftryopt!(self.client).http.get_reaction_users(
self.channel_id.0,
self.message_id.0,
&reaction_type.into(),
- limit.unwrap_or(50),
+ limit,
after.map(|u| u.into().0),
)
}
diff --git a/src/model/error.rs b/src/model/error.rs
index ea919bc..58fe647 100644
--- a/src/model/error.rs
+++ b/src/model/error.rs
@@ -73,6 +73,9 @@ pub enum Error {
/// When attempting to delete below or above the minimum and maximum allowed
/// number of messages.
BulkDeleteAmount,
+ /// The client wasn't present on the model when it was expected to be, e.g.
+ /// when performing a cache or HTTP operation on it.
+ ClientNotPresent,
/// When attempting to delete a number of days' worth of messages that is
/// not allowed.
DeleteMessageDaysAmount(u8),
@@ -131,6 +134,7 @@ impl StdError for Error {
fn description(&self) -> &str {
match *self {
Error::BulkDeleteAmount => "Too few/many messages to bulk delete",
+ Error::ClientNotPresent => "The client wasn't present on the model",
Error::DeleteMessageDaysAmount(_) => "Invalid delete message days",
Error::EmbedTooLarge(_) => "Embed too large",
Error::GuildNotFound => "Guild not found in the cache",
diff --git a/src/model/event.rs b/src/model/event.rs
index a47d36b..fe710da 100644
--- a/src/model/event.rs
+++ b/src/model/event.rs
@@ -10,15 +10,6 @@ use super::prelude::*;
use constants::{OpCode, VoiceOpCode};
use internal::prelude::*;
-#[cfg(feature = "cache")]
-use cache::{Cache, CacheUpdate};
-#[cfg(feature = "cache")]
-use internal::RwLockExt;
-#[cfg(feature = "cache")]
-use std::collections::hash_map::Entry;
-#[cfg(feature = "cache")]
-use std::mem;
-
/// Event data for the channel creation event.
///
/// This is fired when:
@@ -52,107 +43,11 @@ impl Serialize for ChannelCreateEvent {
}
}
-#[cfg(feature = "cache")]
-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 = Arc::clone(group);
-
- let channel_id = group.with_mut(|writer| {
- for (recipient_id, recipient) in &mut writer.recipients {
- cache.update_user_entry(&recipient.read());
-
- *recipient = Arc::clone(&cache.users[recipient_id]);
- }
-
- writer.channel_id
- });
-
- let ch = cache.groups.insert(channel_id, group);
-
- ch.map(Channel::Group)
- },
- Channel::Guild(ref channel) => {
- let (guild_id, channel_id) = channel.with(|channel| (channel.guild_id, channel.id));
-
- cache.channels.insert(channel_id, Arc::clone(channel));
-
- cache
- .guilds
- .get_mut(&guild_id)
- .and_then(|guild| {
- guild
- .with_mut(|guild| guild.channels.insert(channel_id, Arc::clone(channel)))
- })
- .map(Channel::Guild)
- },
- Channel::Private(ref channel) => {
- if let Some(channel) = cache.private_channels.get(&channel.with(|c| c.id)) {
- return Some(Channel::Private(Arc::clone(&(*channel))));
- }
-
- let channel = Arc::clone(channel);
-
- let id = channel.with_mut(|writer| {
- let user_id = writer.recipient.with_mut(|user| {
- cache.update_user_entry(user);
-
- user.id
- });
-
- writer.recipient = Arc::clone(&cache.users[&user_id]);
- writer.id
- });
-
- let ch = cache.private_channels.insert(id, Arc::clone(&channel));
- ch.map(Channel::Private)
- },
- Channel::Category(ref category) => cache
- .categories
- .insert(category.read().id, Arc::clone(category))
- .map(Channel::Category),
- }
- }
-}
-
#[derive(Clone, Debug)]
pub struct ChannelDeleteEvent {
pub channel: Channel,
}
-#[cfg(feature = "cache")]
-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) = channel.with(|channel| (channel.guild_id, channel.id));
-
- cache.channels.remove(&channel_id);
-
- cache
- .guilds
- .get_mut(&guild_id)
- .and_then(|guild| guild.with_mut(|g| g.channels.remove(&channel_id)));
- },
- Channel::Category(ref category) => {
- let channel_id = category.with(|cat| cat.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<'de> Deserialize<'de> for ChannelDeleteEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
Ok(Self {
@@ -174,143 +69,23 @@ pub struct ChannelPinsUpdateEvent {
pub last_pin_timestamp: Option<DateTime<FixedOffset>>,
}
-#[cfg(feature = "cache")]
-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.with_mut(|c| {
- c.last_pin_timestamp = self.last_pin_timestamp;
- });
-
- return None;
- }
-
- if let Some(channel) = cache.private_channels.get_mut(&self.channel_id) {
- channel.with_mut(|c| {
- c.last_pin_timestamp = self.last_pin_timestamp;
- });
-
- return None;
- }
-
- if let Some(group) = cache.groups.get_mut(&self.channel_id) {
- group.with_mut(|c| {
- c.last_pin_timestamp = self.last_pin_timestamp;
- });
-
- return None;
- }
-
- None
- }
-}
-
-
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ChannelRecipientAddEvent {
pub channel_id: ChannelId,
pub user: User,
}
-#[cfg(feature = "cache")]
-impl CacheUpdate for ChannelRecipientAddEvent {
- type Output = ();
-
- fn update(&mut self, cache: &mut Cache) -> Option<()> {
- cache.update_user_entry(&self.user);
- let user = Arc::clone(&cache.users[&self.user.id]);
-
- cache.groups.get_mut(&self.channel_id).map(|group| {
- group.write().recipients.insert(self.user.id, user);
- });
-
- None
- }
-}
-
-
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ChannelRecipientRemoveEvent {
pub channel_id: ChannelId,
pub user: User,
}
-#[cfg(feature = "cache")]
-impl CacheUpdate for ChannelRecipientRemoveEvent {
- type Output = ();
-
- fn update(&mut self, cache: &mut Cache) -> Option<()> {
- cache.groups.get_mut(&self.channel_id).map(|group| {
- group.with_mut(|g| g.recipients.remove(&self.user.id))
- });
-
- None
- }
-}
-
#[derive(Clone, Debug)]
pub struct ChannelUpdateEvent {
pub channel: Channel,
}
-#[cfg(feature = "cache")]
-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) =
- group.with(|g| (g.channel_id, g.recipients.is_empty()));
-
- match cache.groups.entry(ch_id) {
- Entry::Vacant(e) => {
- e.insert(Arc::clone(group));
- },
- Entry::Occupied(mut e) => {
- let mut dest = e.get_mut().write();
-
- if no_recipients {
- let recipients = mem::replace(&mut dest.recipients, HashMap::new());
-
- dest.clone_from(&group.read());
-
- dest.recipients = recipients;
- } else {
- dest.clone_from(&group.read());
- }
- },
- }
- },
- Channel::Guild(ref channel) => {
- let (guild_id, channel_id) = channel.with(|channel| (channel.guild_id, channel.id));
-
- cache.channels.insert(channel_id, Arc::clone(channel));
- cache.guilds.get_mut(&guild_id).map(|guild| {
- guild
- .with_mut(|g| g.channels.insert(channel_id, Arc::clone(channel)))
- });
- },
- Channel::Private(ref channel) => {
- cache
- .private_channels
- .get_mut(&channel.read().id)
- .map(|private| private.clone_from(channel));
- },
- Channel::Category(ref category) => {
- cache
- .categories
- .get_mut(&category.read().id)
- .map(|c| c.clone_from(category));
- },
- }
-
- None
- }
-}
-
impl<'de> Deserialize<'de> for ChannelUpdateEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
Ok(Self {
@@ -343,31 +118,6 @@ pub struct GuildCreateEvent {
pub guild: Guild,
}
-#[cfg(feature = "cache")]
-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 {
- cache.update_user_entry(&member.user.read());
- let user = Arc::clone(&cache.users[user_id]);
-
- member.user = Arc::clone(&user);
- }
-
- cache.channels.extend(guild.channels.clone());
- cache
- .guilds
- .insert(self.guild.id, Arc::new(RwLock::new(guild)));
-
- None
- }
-}
-
impl<'de> Deserialize<'de> for GuildCreateEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
Ok(Self {
@@ -388,22 +138,6 @@ pub struct GuildDeleteEvent {
pub guild: PartialGuild,
}
-#[cfg(feature = "cache")]
-impl CacheUpdate for GuildDeleteEvent {
- type Output = Arc<RwLock<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.write().channels.keys() {
- cache.channels.remove(channel_id);
- }
-
- guild
- })
- }
-}
-
impl<'de> Deserialize<'de> for GuildDeleteEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
Ok(Self {
@@ -425,21 +159,6 @@ pub struct GuildEmojisUpdateEvent {
pub guild_id: GuildId,
}
-#[cfg(feature = "cache")]
-impl CacheUpdate for GuildEmojisUpdateEvent {
- type Output = ();
-
- fn update(&mut self, cache: &mut Cache) -> Option<()> {
- cache.guilds.get_mut(&self.guild_id).map(|guild| {
- guild.with_mut(|g| {
- g.emojis.clone_from(&self.emojis)
- });
- });
-
- None
- }
-}
-
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct GuildIntegrationsUpdateEvent {
pub guild_id: GuildId,
@@ -451,28 +170,6 @@ pub struct GuildMemberAddEvent {
pub member: Member,
}
-#[cfg(feature = "cache")]
-impl CacheUpdate for GuildMemberAddEvent {
- type Output = ();
-
- fn update(&mut self, cache: &mut Cache) -> Option<()> {
- let user_id = self.member.user.with(|u| u.id);
- cache.update_user_entry(&self.member.user.read());
-
- // Always safe due to being inserted above.
- self.member.user = Arc::clone(&cache.users[&user_id]);
-
- cache.guilds.get_mut(&self.guild_id).map(|guild| {
- guild.with_mut(|guild| {
- guild.member_count += 1;
- guild.members.insert(user_id, self.member.clone());
- })
- });
-
- None
- }
-}
-
impl<'de> Deserialize<'de> for GuildMemberAddEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let map = JsonMap::deserialize(deserializer)?;
@@ -496,20 +193,6 @@ pub struct GuildMemberRemoveEvent {
pub user: User,
}
-#[cfg(feature = "cache")]
-impl CacheUpdate for GuildMemberRemoveEvent {
- type Output = Member;
-
- fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> {
- cache.guilds.get_mut(&self.guild_id).and_then(|guild| {
- guild.with_mut(|guild| {
- guild.member_count -= 1;
- guild.members.remove(&self.user.id)
- })
- })
- }
-}
-
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct GuildMemberUpdateEvent {
pub guild_id: GuildId,
@@ -518,77 +201,12 @@ pub struct GuildMemberUpdateEvent {
pub user: User,
}
-#[cfg(feature = "cache")]
-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.write();
-
- let mut found = false;
-
- let item = if let Some(member) = guild.members.get_mut(&self.user.id) {
- let item = Some(member.clone());
-
- member.nick.clone_from(&self.nick);
- member.roles.clone_from(&self.roles);
- member.user.write().clone_from(&self.user);
-
- found = true;
-
- item
- } else {
- None
- };
-
- if !found {
- guild.members.insert(
- self.user.id,
- Member {
- deaf: false,
- guild_id: self.guild_id,
- joined_at: None,
- mute: false,
- nick: self.nick.clone(),
- roles: self.roles.clone(),
- user: Arc::new(RwLock::new(self.user.clone())),
- },
- );
- }
-
- item
- } else {
- None
- }
- }
-}
-
#[derive(Clone, Debug, Serialize)]
pub struct GuildMembersChunkEvent {
pub guild_id: GuildId,
pub members: HashMap<UserId, Member>,
}
-#[cfg(feature = "cache")]
-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.read());
- }
-
- cache.guilds.get_mut(&self.guild_id).map(|guild| {
- guild.with_mut(|g| g.members.extend(self.members.clone()))
- });
-
- None
- }
-}
-
impl<'de> Deserialize<'de> for GuildMembersChunkEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let mut map = JsonMap::deserialize(deserializer)?;
@@ -627,105 +245,28 @@ pub struct GuildRoleCreateEvent {
pub role: Role,
}
-#[cfg(feature = "cache")]
-impl CacheUpdate for GuildRoleCreateEvent {
- type Output = ();
-
- fn update(&mut self, cache: &mut Cache) -> Option<()> {
- cache.guilds.get_mut(&self.guild_id).map(|guild| {
- guild
- .write()
- .roles
- .insert(self.role.id, self.role.clone())
- });
-
- None
- }
-}
-
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct GuildRoleDeleteEvent {
pub guild_id: GuildId,
pub role_id: RoleId,
}
-#[cfg(feature = "cache")]
-impl CacheUpdate for GuildRoleDeleteEvent {
- type Output = Role;
-
- fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> {
- cache
- .guilds
- .get_mut(&self.guild_id)
- .and_then(|guild| guild.with_mut(|g| g.roles.remove(&self.role_id)))
- }
-}
-
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct GuildRoleUpdateEvent {
pub guild_id: GuildId,
pub role: Role,
}
-#[cfg(feature = "cache")]
-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| {
- guild.with_mut(|g| {
- g.roles
- .get_mut(&self.role.id)
- .map(|role| mem::replace(role, self.role.clone()))
- })
- })
- }
-}
-
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct GuildUnavailableEvent {
#[serde(rename = "id")] pub guild_id: GuildId,
}
-#[cfg(feature = "cache")]
-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
- }
-}
-
#[derive(Clone, Debug)]
pub struct GuildUpdateEvent {
pub guild: PartialGuild,
}
-#[cfg(feature = "cache")]
-impl CacheUpdate for GuildUpdateEvent {
- type Output = ();
-
- fn update(&mut self, cache: &mut Cache) -> Option<()> {
- cache.guilds.get_mut(&self.guild.id).map(|guild| {
- let mut guild = guild.write();
-
- 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<'de> Deserialize<'de> for GuildUpdateEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
Ok(Self {
@@ -799,62 +340,6 @@ pub struct PresenceUpdateEvent {
pub roles: Option<Vec<RoleId>>,
}
-#[cfg(feature = "cache")]
-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.read());
- *user = Arc::clone(&cache.users[&user_id]);
- }
-
- if let Some(guild_id) = self.guild_id {
- if let Some(guild) = cache.guilds.get_mut(&guild_id) {
- let mut guild = guild.write();
-
- // 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, 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, Member {
- deaf: false,
- guild_id: guild_id,
- joined_at: None,
- mute: false,
- nick: self.presence.nick.clone(),
- user: Arc::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, self.presence.clone());
- }
-
- None
- }
-}
-
impl<'de> Deserialize<'de> for PresenceUpdateEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let mut map = JsonMap::deserialize(deserializer)?;
@@ -885,25 +370,6 @@ pub struct PresencesReplaceEvent {
pub presences: Vec<Presence>,
}
-#[cfg(feature = "cache")]
-impl CacheUpdate for PresencesReplaceEvent {
- type Output = ();
-
- fn update(&mut self, cache: &mut Cache) -> Option<()> {
- cache.presences.extend({
- let mut p: HashMap<UserId, Presence> = HashMap::default();
-
- for presence in &self.presences {
- p.insert(presence.user_id, presence.clone());
- }
-
- p
- });
-
- None
- }
-}
-
impl<'de> Deserialize<'de> for PresencesReplaceEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let presences: Vec<Presence> = Deserialize::deserialize(deserializer)?;
@@ -979,46 +445,6 @@ pub struct ReadyEvent {
pub ready: Ready,
}
-#[cfg(feature = "cache")]
-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, Arc::new(RwLock::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 {
- if let Some(ref user) = presence.user {
- cache.update_user_entry(&user.read());
- }
-
- 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<'de> Deserialize<'de> for ReadyEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
Ok(Self {
@@ -1057,15 +483,6 @@ pub struct UserUpdateEvent {
pub current_user: CurrentUser,
}
-#[cfg(feature = "cache")]
-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<'de> Deserialize<'de> for UserUpdateEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
Ok(Self {
@@ -1095,43 +512,6 @@ pub struct VoiceStateUpdateEvent {
pub voice_state: VoiceState,
}
-#[cfg(feature = "cache")]
-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.write();
-
- 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
- }
-}
-
impl<'de> Deserialize<'de> for VoiceStateUpdateEvent {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let map = JsonMap::deserialize(deserializer)?;
diff --git a/src/model/gateway.rs b/src/model/gateway.rs
index 6ad2cda..ad30bab 100644
--- a/src/model/gateway.rs
+++ b/src/model/gateway.rs
@@ -1,10 +1,10 @@
//! Models pertaining to the gateway.
-use parking_lot::RwLock;
use serde::de::Error as DeError;
use serde::ser::{SerializeStruct, Serialize, Serializer};
use serde_json;
-use std::sync::Arc;
+use std::cell::RefCell;
+use std::rc::Rc;
use super::utils::*;
use super::prelude::*;
@@ -209,7 +209,7 @@ pub struct Presence {
/// date.
pub user_id: UserId,
/// The associated user instance.
- pub user: Option<Arc<RwLock<User>>>,
+ pub user: Option<Rc<RefCell<User>>>,
}
impl<'de> Deserialize<'de> for Presence {
@@ -224,7 +224,7 @@ impl<'de> Deserialize<'de> for Presence {
let user = User::deserialize(Value::Object(user_map))
.map_err(DeError::custom)?;
- (user.id, Some(Arc::new(RwLock::new(user))))
+ (user.id, Some(Rc::new(RefCell::new(user))))
} else {
let user_id = user_map
.remove("id")
@@ -280,7 +280,7 @@ impl Serialize for Presence {
state.serialize_field("status", &self.status)?;
if let Some(ref user) = self.user {
- state.serialize_field("user", &*user.read())?;
+ state.serialize_field("user", &*user.borrow())?;
} else {
state.serialize_field("user", &UserId {
id: self.user_id.0,
@@ -295,10 +295,14 @@ impl Serialize for Presence {
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Ready {
pub guilds: Vec<GuildStatus>,
- #[serde(default, deserialize_with = "deserialize_presences")]
- pub presences: HashMap<UserId, Presence>,
- #[serde(default, deserialize_with = "deserialize_private_channels")]
- pub private_channels: HashMap<ChannelId, Channel>,
+ #[serde(default,
+ deserialize_with = "deserialize_presences",
+ serialize_with = "serialize_gen_rc_map")]
+ pub presences: HashMap<UserId, Rc<RefCell<Presence>>>,
+ #[serde(default,
+ deserialize_with = "deserialize_private_channels",
+ serialize_with = "serialize_gen_rc_map")]
+ pub private_channels: HashMap<ChannelId, Rc<RefCell<Channel>>>,
pub session_id: String,
pub shard: Option<[u64; 2]>,
#[serde(default, rename = "_trace")]
diff --git a/src/model/guild/emoji.rs b/src/model/guild/emoji.rs
index 23a174d..48d9b7d 100644
--- a/src/model/guild/emoji.rs
+++ b/src/model/guild/emoji.rs
@@ -1,16 +1,17 @@
use std::fmt::{Display, Formatter, Result as FmtResult, Write as FmtWrite};
use super::super::id::{EmojiId, RoleId};
+use super::super::WrappedClient;
+#[cfg(feature = "cache")]
+use futures::{Future, future};
#[cfg(all(feature = "cache", feature = "model"))]
use internal::prelude::*;
#[cfg(all(feature = "cache", feature = "model"))]
-use std::mem;
-#[cfg(all(feature = "cache", feature = "model"))]
use super::super::ModelError;
#[cfg(all(feature = "cache", feature = "model"))]
use super::super::id::GuildId;
-#[cfg(all(feature = "cache", feature = "model"))]
-use {CACHE, http};
+#[cfg(feature = "cache")]
+use ::FutureResult;
/// 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
@@ -37,6 +38,8 @@ pub struct Emoji {
///
/// [`Role`]: struct.Role.html
pub roles: Vec<RoleId>,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
@@ -73,11 +76,15 @@ impl Emoji {
/// }
/// ```
#[cfg(feature = "cache")]
- pub fn delete(&self) -> Result<()> {
- match self.find_guild_id() {
- Some(guild_id) => http::delete_emoji(guild_id.0, self.id.0),
- None => Err(Error::Model(ModelError::ItemMissing)),
- }
+ pub fn delete(&self) -> FutureResult<()> {
+ let guild_id = match self.find_guild_id() {
+ Some(guild_id) => guild_id,
+ None => return Box::new(future::err(Error::Model(
+ ModelError::ItemMissing,
+ ))),
+ };
+
+ ftryopt!(self.client).http.delete_emoji(guild_id.0, self.id.0)
}
/// Edits the emoji by updating it with a new name.
@@ -110,24 +117,16 @@ impl Emoji {
/// assert_eq!(emoji.name, "blobuwu");
/// ```
#[cfg(feature = "cache")]
- pub fn edit(&mut self, name: &str) -> Result<()> {
- match self.find_guild_id() {
- Some(guild_id) => {
- let map = json!({
- "name": name,
- });
-
- match http::edit_emoji(guild_id.0, self.id.0, &map) {
- Ok(emoji) => {
- mem::replace(self, emoji);
+ pub fn edit<'a>(&'a mut self, name: &'a str)
+ -> Box<Future<Item = Emoji, Error = Error> + 'a> {
+ let guild_id = match self.find_guild_id() {
+ Some(guild_id) => guild_id,
+ None => return Box::new(future::err(Error::Model(
+ ModelError::ItemMissing,
+ ))),
+ };
- Ok(())
- },
- Err(why) => Err(why),
- }
- },
- None => Err(Error::Model(ModelError::ItemMissing)),
- }
+ ftryopt!(self.client).http.edit_emoji(guild_id.0, self.id.0, name)
}
/// Finds the [`Guild`] that owns the emoji by looking through the Cache.
@@ -158,8 +157,8 @@ impl Emoji {
/// ```
#[cfg(feature = "cache")]
pub fn find_guild_id(&self) -> Option<GuildId> {
- for guild in CACHE.read().guilds.values() {
- let guild = guild.read();
+ for guild in self.client.as_ref()?.cache.borrow().guilds.values() {
+ let guild = guild.borrow();
if guild.emojis.contains_key(&self.id) {
return Some(guild.id);
diff --git a/src/model/guild/guild_id.rs b/src/model/guild/guild_id.rs
index adfac05..ea095a0 100644
--- a/src/model/guild/guild_id.rs
+++ b/src/model/guild/guild_id.rs
@@ -1,16 +1,5 @@
use model::prelude::*;
-#[cfg(all(feature = "cache", feature = "model"))]
-use CACHE;
-#[cfg(feature = "model")]
-use builder::{EditGuild, EditMember, EditRole};
-#[cfg(feature = "model")]
-use internal::prelude::*;
-#[cfg(feature = "model")]
-use model::guild::BanOptions;
-#[cfg(feature = "model")]
-use {http, utils};
-
#[cfg(feature = "model")]
impl GuildId {
/// Converts the guild Id into the default channel's Id.
@@ -18,459 +7,10 @@ impl GuildId {
#[deprecated(note = "The concept of default channels is no more, use \
`Guild::default_channel{_guaranteed}` to simulate the
concept.")]
- pub fn as_channel_id(&self) -> ChannelId { ChannelId(self.0) }
-
- /// Ban a [`User`] from the guild. All messages by the
- /// user within the last given number of days given will be deleted.
- ///
- /// Refer to the documentation for [`Guild::ban`] for more information.
- ///
- /// **Note**: Requires the [Ban Members] permission.
- ///
- /// # Examples
- ///
- /// Ban a member and remove all messages they've sent in the last 4 days:
- ///
- /// ```rust,ignore
- /// use serenity::model::GuildId;
- ///
- /// // assuming a `user` has already been bound
- /// let _ = GuildId(81384788765712384).ban(user, 4);
- /// ```
- ///
- /// # Errors
- ///
- /// Returns a [`ModelError::DeleteMessageDaysAmount`] if the number of
- /// days' worth of messages to delete is over the maximum.
- ///
- /// [`ModelError::DeleteMessageDaysAmount`]:
- /// enum.ModelError.html#variant.DeleteMessageDaysAmount
- /// [`Guild::ban`]: struct.Guild.html#method.ban
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- pub fn ban<U, BO>(&self, user: U, ban_options: &BO) -> Result<()>
- where U: Into<UserId>, BO: BanOptions {
- let dmd = ban_options.dmd();
- if dmd > 7 {
- return Err(Error::Model(ModelError::DeleteMessageDaysAmount(dmd)));
- }
-
- let reason = ban_options.reason();
-
- if reason.len() > 512 {
- return Err(Error::ExceededLimit(reason.to_string(), 512));
- }
-
- http::ban_user(self.0, user.into().0, dmd, &*reason)
- }
-
- /// Gets a list of the guild's bans.
- ///
- /// Requires the [Ban Members] permission.
- ///
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- #[inline]
- pub fn bans(&self) -> Result<Vec<Ban>> { http::get_bans(self.0) }
-
- /// Gets a list of the guild's audit log entries
- #[inline]
- pub fn audit_logs(&self, action_type: Option<u8>,
- user_id: Option<UserId>,
- before: Option<AuditLogEntryId>,
- limit: Option<u8>) -> Result<AuditLogs> {
- http::get_audit_logs(self.0, action_type, user_id.map(|u| u.0), before.map(|a| a.0), limit)
- }
-
- /// Gets all of the guild's channels over the REST API.
- ///
- /// [`Guild`]: struct.Guild.html
- pub fn channels(&self) -> Result<HashMap<ChannelId, GuildChannel>> {
- let mut channels = HashMap::new();
-
- for channel in http::get_channels(self.0)? {
- channels.insert(channel.id, channel);
- }
-
- Ok(channels)
- }
-
- /// Creates a [`GuildChannel`] in the the guild.
- ///
- /// Refer to [`http::create_channel`] for more information.
- ///
- /// Requires the [Manage Channels] permission.
- ///
- /// # Examples
- ///
- /// Create a voice channel in a guild with the name `test`:
- ///
- /// ```rust,ignore
- /// use serenity::model::{ChannelType, GuildId};
- ///
- /// let _channel = GuildId(7).create_channel("test", ChannelType::Voice, None);
- /// ```
- ///
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [`http::create_channel`]: ../http/fn.create_channel.html
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
- pub fn create_channel<C>(&self, name: &str, kind: ChannelType, category: C) -> Result<GuildChannel>
- where C: Into<Option<ChannelId>> {
- let map = json!({
- "name": name,
- "type": kind as u8,
- "parent_id": category.into().map(|c| c.0)
- });
-
- http::create_channel(self.0, &map)
- }
-
- /// Creates an emoji in the guild with a name and base64-encoded image.
- ///
- /// Refer to the documentation for [`Guild::create_emoji`] for more
- /// information.
- ///
- /// Requires the [Manage Emojis] permission.
- ///
- /// # Examples
- ///
- /// See the [`EditProfile::avatar`] example for an in-depth example as to
- /// how to read an image from the filesystem and encode it as base64. Most
- /// of the example can be applied similarly for this method.
- ///
- /// [`EditProfile::avatar`]: ../builder/struct.EditProfile.html#method.avatar
- /// [`Guild::create_emoji`]: struct.Guild.html#method.create_emoji
- /// [`utils::read_image`]: ../utils/fn.read_image.html
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
- pub fn create_emoji(&self, name: &str, image: &str) -> Result<Emoji> {
- let map = json!({
- "name": name,
- "image": image,
- });
-
- http::create_emoji(self.0, &map)
- }
-
- /// Creates an integration for the guild.
- ///
- /// Requires the [Manage Guild] permission.
- ///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- pub fn create_integration<I>(&self, integration_id: I, kind: &str) -> Result<()>
- where I: Into<IntegrationId> {
- let integration_id = integration_id.into();
- let map = json!({
- "id": integration_id.0,
- "type": kind,
- });
-
- http::create_guild_integration(self.0, integration_id.0, &map)
- }
-
- /// Creates a new role in the guild with the data set, if any.
- ///
- /// See the documentation for [`Guild::create_role`] on how to use this.
- ///
- /// **Note**: Requires the [Manage Roles] permission.
- ///
- /// [`Guild::create_role`]: struct.Guild.html#method.create_role
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
- #[inline]
- pub fn create_role<F: FnOnce(EditRole) -> EditRole>(&self, f: F) -> Result<Role> {
- let map = utils::vecmap_to_json_map(f(EditRole::default()).0);
-
- let role = http::create_role(self.0, &map)?;
-
- if let Some(position) = map.get("position").and_then(Value::as_u64) {
- self.edit_role_position(role.id, position)?;
- }
-
- Ok(role)
- }
-
- /// Deletes the current guild if the current account is the owner of the
- /// guild.
- ///
- /// Refer to [`Guild::delete`] for more information.
- ///
- /// **Note**: Requires the current user to be the owner of the guild.
- ///
- /// [`Guild::delete`]: struct.Guild.html#method.delete
- #[inline]
- pub fn delete(&self) -> Result<PartialGuild> { http::delete_guild(self.0) }
-
- /// Deletes an [`Emoji`] from the guild.
- ///
- /// Requires the [Manage Emojis] permission.
- ///
- /// [`Emoji`]: struct.Emoji.html
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
- #[inline]
- pub fn delete_emoji<E: Into<EmojiId>>(&self, emoji_id: E) -> Result<()> {
- http::delete_emoji(self.0, emoji_id.into().0)
- }
-
- /// Deletes an integration by Id from the guild.
- ///
- /// Requires the [Manage Guild] permission.
- ///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- #[inline]
- pub fn delete_integration<I: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
- http::delete_guild_integration(self.0, integration_id.into().0)
- }
-
- /// Deletes a [`Role`] by Id from the guild.
- ///
- /// Also see [`Role::delete`] if you have the `cache` and `methods` features
- /// enabled.
- ///
- /// Requires the [Manage Roles] permission.
- ///
- /// [`Role`]: struct.Role.html
- /// [`Role::delete`]: struct.Role.html#method.delete
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
- #[inline]
- pub fn delete_role<R: Into<RoleId>>(&self, role_id: R) -> Result<()> {
- http::delete_role(self.0, role_id.into().0)
- }
-
- /// Edits the current guild with new data where specified.
- ///
- /// Refer to [`Guild::edit`] for more information.
- ///
- /// **Note**: Requires the current user to have the [Manage Guild]
- /// permission.
- ///
- /// [`Guild::edit`]: struct.Guild.html#method.edit
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- #[inline]
- pub fn edit<F: FnOnce(EditGuild) -> EditGuild>(&mut self, f: F) -> Result<PartialGuild> {
- let map = utils::vecmap_to_json_map(f(EditGuild::default()).0);
-
- http::edit_guild(self.0, &map)
- }
-
- /// Edits an [`Emoji`]'s name in the guild.
- ///
- /// Also see [`Emoji::edit`] if you have the `cache` and `methods` features
- /// enabled.
- ///
- /// Requires the [Manage Emojis] permission.
- ///
- /// [`Emoji`]: struct.Emoji.html
- /// [`Emoji::edit`]: struct.Emoji.html#method.edit
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
- pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str) -> Result<Emoji> {
- let map = json!({
- "name": name,
- });
-
- http::edit_emoji(self.0, emoji_id.into().0, &map)
- }
-
- /// Edits the properties of member of the guild, such as muting or
- /// nicknaming them.
- ///
- /// Refer to `EditMember`'s documentation for a full list of methods and
- /// permission restrictions.
- ///
- /// # Examples
- ///
- /// Mute a member and set their roles to just one role with a predefined Id:
- ///
- /// ```rust,ignore
- /// guild.edit_member(user_id, |m| m.mute(true).roles(&vec![role_id]));
- /// ```
- #[inline]
- pub fn edit_member<F, U>(&self, user_id: U, f: F) -> Result<()>
- where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> {
- let map = utils::vecmap_to_json_map(f(EditMember::default()).0);
-
- http::edit_member(self.0, user_id.into().0, &map)
+ pub fn as_channel_id(&self) -> ChannelId {
+ ChannelId(self.0)
}
- /// Edits the current user's nickname for the guild.
- ///
- /// Pass `None` to reset the nickname.
- ///
- /// Requires the [Change Nickname] permission.
- ///
- /// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html
- #[inline]
- pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
- http::edit_nickname(self.0, new_nickname)
- }
-
- /// Edits a [`Role`], optionally setting its new fields.
- ///
- /// Requires the [Manage Roles] permission.
- ///
- /// # Examples
- ///
- /// Make a role hoisted:
- ///
- /// ```rust,ignore
- /// use serenity::model::{GuildId, RoleId};
- ///
- /// GuildId(7).edit_role(RoleId(8), |r| r.hoist(true));
- /// ```
- ///
- /// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
- #[inline]
- pub fn edit_role<F, R>(&self, role_id: R, f: F) -> Result<Role>
- where F: FnOnce(EditRole) -> EditRole, R: Into<RoleId> {
- let map = utils::vecmap_to_json_map(f(EditRole::default()).0);
-
- http::edit_role(self.0, role_id.into().0, &map)
- }
-
- /// Edits the order of [`Role`]s
- /// Requires the [Manage Roles] permission.
- ///
- /// # Examples
- ///
- /// Change the order of a role:
- ///
- /// ```rust,ignore
- /// use serenity::model::{GuildId, RoleId};
- /// GuildId(7).edit_role_position(RoleId(8), 2);
- /// ```
- ///
- /// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
- #[inline]
- pub fn edit_role_position<R>(&self, role_id: R, position: u64) -> Result<Vec<Role>>
- where R: Into<RoleId> {
- http::edit_role_position(self.0, role_id.into().0, position)
- }
-
-
- /// Search the cache for the guild.
- #[cfg(feature = "cache")]
- pub fn find(&self) -> Option<Arc<RwLock<Guild>>> { CACHE.read().guild(*self) }
-
- /// Requests the guild over REST.
- ///
- /// Note that this will not be a complete guild, as REST does not send
- /// all data with a guild retrieval.
- #[inline]
- pub fn get(&self) -> Result<PartialGuild> { http::get_guild(self.0) }
-
- /// Gets all integration of the guild.
- ///
- /// This performs a request over the REST API.
- #[inline]
- pub fn integrations(&self) -> Result<Vec<Integration>> { http::get_guild_integrations(self.0) }
-
- /// Gets all of the guild's invites.
- ///
- /// Requires the [Manage Guild] permission.
- ///
- /// [Manage Guild]: permissions/struct.MANAGE_GUILD.html
- #[inline]
- pub fn invites(&self) -> Result<Vec<RichInvite>> { http::get_guild_invites(self.0) }
-
- /// Kicks a [`Member`] from the guild.
- ///
- /// Requires the [Kick Members] permission.
- ///
- /// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
- #[inline]
- pub fn kick<U: Into<UserId>>(&self, user_id: U) -> Result<()> {
- http::kick_member(self.0, user_id.into().0)
- }
-
- /// Leaves the guild.
- #[inline]
- pub fn leave(&self) -> Result<()> { http::leave_guild(self.0) }
-
- /// Gets a user's [`Member`] for the guild by Id.
- ///
- /// [`Guild`]: struct.Guild.html
- /// [`Member`]: struct.Member.html
- #[inline]
- pub fn member<U: Into<UserId>>(&self, user_id: U) -> Result<Member> {
- http::get_member(self.0, user_id.into().0)
- }
-
- /// Gets a list of the guild's members.
- ///
- /// Optionally pass in the `limit` to limit the number of results. Maximum
- /// value is 1000. Optionally pass in `after` to offset the results by a
- /// [`User`]'s Id.
- ///
- /// [`User`]: struct.User.html
- #[inline]
- pub fn members<U>(&self, limit: Option<u64>, after: Option<U>) -> Result<Vec<Member>>
- where U: Into<UserId> {
- http::get_guild_members(self.0, limit, after.map(|x| x.into().0))
- }
-
- /// Moves a member to a specific voice channel.
- ///
- /// Requires the [Move Members] permission.
- ///
- /// [Move Members]: permissions/constant.MOVE_MEMBERS.html
- pub fn move_member<C, U>(&self, user_id: U, channel_id: C) -> Result<()>
- where C: Into<ChannelId>, U: Into<UserId> {
- let mut map = Map::new();
- map.insert(
- "channel_id".to_string(),
- Value::Number(Number::from(channel_id.into().0)),
- );
-
- http::edit_member(self.0, user_id.into().0, &map)
- }
-
- /// Gets the number of [`Member`]s that would be pruned with the given
- /// number of days.
- ///
- /// Requires the [Kick Members] permission.
- ///
- /// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
- pub fn prune_count(&self, days: u16) -> Result<GuildPrune> {
- let map = json!({
- "days": days,
- });
-
- http::get_guild_prune_count(self.0, &map)
- }
-
- /// Re-orders the channels of the guild.
- ///
- /// Accepts an iterator of a tuple of the channel ID to modify and its new
- /// position.
- ///
- /// Although not required, you should specify all channels' positions,
- /// regardless of whether they were updated. Otherwise, positioning can
- /// sometimes get weird.
- pub fn reorder_channels<It>(&self, channels: It) -> Result<()>
- where It: IntoIterator<Item = (ChannelId, u64)> {
- let items = channels.into_iter().map(|(id, pos)| json!({
- "id": id,
- "position": pos,
- })).collect();
-
- http::edit_guild_channel_positions(self.0, &Value::Array(items))
- }
-
- /// Returns the Id of the shard associated with the guild.
- ///
- /// When the cache is enabled this will automatically retrieve the total
- /// number of shards.
- ///
- /// **Note**: When the cache is enabled, this function unlocks the cache to
- /// retrieve the total number of shards in use. If you already have the
- /// total, consider using [`utils::shard_id`].
- ///
- /// [`utils::shard_id`]: ../utils/fn.shard_id.html
- #[cfg(all(feature = "cache", feature = "utils"))]
- #[inline]
- pub fn shard_id(&self) -> u64 { ::utils::shard_id(self.0, CACHE.read().shard_count) }
-
/// Returns the Id of the shard associated with the guild.
///
/// When the cache is enabled this will automatically retrieve the total
@@ -492,56 +32,11 @@ impl GuildId {
///
/// assert_eq!(guild_id.shard_id(17), 7);
/// ```
- #[cfg(all(feature = "utils", not(feature = "cache")))]
- #[inline]
- pub fn shard_id(&self, shard_count: u64) -> u64 { ::utils::shard_id(self.0, shard_count) }
-
- /// Starts an integration sync for the given integration Id.
- ///
- /// Requires the [Manage Guild] permission.
- ///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- #[inline]
- pub fn start_integration_sync<I: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
- http::start_integration_sync(self.0, integration_id.into().0)
- }
-
- /// Starts a prune of [`Member`]s.
- ///
- /// See the documentation on [`GuildPrune`] for more information.
- ///
- /// **Note**: Requires the [Kick Members] permission.
- ///
- /// [`GuildPrune`]: struct.GuildPrune.html
- /// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
+ #[cfg(feature = "utils")]
#[inline]
- pub fn start_prune(&self, days: u16) -> Result<GuildPrune> {
- let map = json!({
- "days": days,
- });
-
- http::start_guild_prune(self.0, &map)
+ pub fn shard_id(&self, shard_count: u64) -> u64 {
+ ::utils::shard_id(self.0, shard_count)
}
-
- /// Unbans a [`User`] from the guild.
- ///
- /// Requires the [Ban Members] permission.
- ///
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- #[inline]
- pub fn unban<U: Into<UserId>>(&self, user_id: U) -> Result<()> {
- http::remove_ban(self.0, user_id.into().0)
- }
-
- /// Retrieves the guild's webhooks.
- ///
- /// **Note**: Requires the [Manage Webhooks] permission.
- ///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
- #[inline]
- pub fn webhooks(&self) -> Result<Vec<Webhook>> { http::get_guild_webhooks(self.0) }
}
impl From<PartialGuild> for GuildId {
diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs
index cbc6066..0c22597 100644
--- a/src/model/guild/member.rs
+++ b/src/model/guild/member.rs
@@ -1,7 +1,10 @@
-use model::prelude::*;
use chrono::{DateTime, FixedOffset};
-use std::fmt::{Display, Formatter, Result as FmtResult};
-use super::deserialize_sync_user;
+use futures::future;
+use model::prelude::*;
+use std::cell::RefCell;
+use super::super::WrappedClient;
+use super::deserialize_user;
+use ::FutureResult;
#[cfg(all(feature = "builder", feature = "cache", feature = "model"))]
use builder::EditMember;
@@ -11,8 +14,6 @@ use internal::prelude::*;
use std::borrow::Cow;
#[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
use utils::Colour;
-#[cfg(all(feature = "cache", feature = "model"))]
-use {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 {
@@ -66,60 +67,53 @@ pub struct Member {
/// Vector of Ids of [`Role`]s given to the member.
pub roles: Vec<RoleId>,
/// Attached User struct.
- #[serde(deserialize_with = "deserialize_sync_user",
- serialize_with = "serialize_sync_user")]
- pub user: Arc<RwLock<User>>,
+ #[serde(deserialize_with = "deserialize_user",
+ serialize_with = "serialize_user")]
+ pub user: Rc<RefCell<User>>,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
impl Member {
- /// Adds a [`Role`] to the member, editing its roles in-place if the request
- /// was successful.
+ /// Adds a [`Role`] to the member.
///
/// **Note**: Requires the [Manage Roles] permission.
///
/// [`Role`]: struct.Role.html
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
#[cfg(feature = "cache")]
- pub fn add_role<R: Into<RoleId>>(&mut self, role_id: R) -> Result<()> {
+ pub fn add_role<R: Into<RoleId>>(&mut self, role_id: R)
+ -> FutureResult<()> {
let role_id = role_id.into();
if self.roles.contains(&role_id) {
- return Ok(());
+ return Box::new(future::ok(()));
}
- match http::add_member_role(self.guild_id.0, self.user.read().id.0, role_id.0) {
- Ok(()) => {
- self.roles.push(role_id);
-
- Ok(())
- },
- Err(why) => Err(why),
- }
+ ftryopt!(self.client).http.add_member_role(
+ self.guild_id.0,
+ self.user.borrow().id.0,
+ role_id.0,
+ )
}
- /// Adds one or multiple [`Role`]s to the member, editing
- /// its roles in-place if the request was successful.
+ /// Adds one or multiple [`Role`]s to the member.
///
/// **Note**: Requires the [Manage Roles] permission.
///
/// [`Role`]: struct.Role.html
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
#[cfg(feature = "cache")]
- pub fn add_roles(&mut self, role_ids: &[RoleId]) -> Result<()> {
- self.roles.extend_from_slice(role_ids);
-
- let builder = EditMember::default().roles(&self.roles);
- let map = utils::vecmap_to_json_map(builder.0);
-
- match http::edit_member(self.guild_id.0, self.user.read().id.0, &map) {
- Ok(()) => Ok(()),
- Err(why) => {
- self.roles.retain(|r| !role_ids.contains(r));
+ pub fn add_roles(&mut self, role_ids: &[RoleId]) -> FutureResult<()> {
+ let mut roles = self.roles.clone();
+ roles.extend(role_ids);
- Err(why)
- },
- }
+ ftryopt!(self.client).http.edit_member(
+ self.guild_id.0,
+ self.user.borrow().id.0,
+ |f| f.roles(roles),
+ )
}
/// Ban the member from its guild, deleting the last X number of
@@ -136,21 +130,27 @@ impl Member {
///
/// [Ban Members]: permissions/constant.BAN_MEMBERS.html
#[cfg(feature = "cache")]
- pub fn ban<BO: BanOptions>(&self, ban_options: &BO) -> Result<()> {
+ pub fn ban<BO: BanOptions>(&self, ban_options: &BO) -> FutureResult<()> {
let dmd = ban_options.dmd();
+
if dmd > 7 {
- return Err(Error::Model(ModelError::DeleteMessageDaysAmount(dmd)));
+ return Box::new(future::err(Error::Model(
+ ModelError::DeleteMessageDaysAmount(dmd),
+ )));
}
let reason = ban_options.reason();
if reason.len() > 512 {
- return Err(Error::ExceededLimit(reason.to_string(), 512));
+ return Box::new(future::err(Error::ExceededLimit(
+ reason.to_string(),
+ 512,
+ )));
}
- http::ban_user(
+ ftryopt!(self.client).http.ban_user(
self.guild_id.0,
- self.user.read().id.0,
+ self.user.borrow().id.0,
dmd,
&*reason,
)
@@ -159,38 +159,37 @@ impl Member {
/// Determines the member's colour.
#[cfg(all(feature = "cache", feature = "utils"))]
pub fn colour(&self) -> Option<Colour> {
- let cache = CACHE.read();
- let guild = cache.guilds.get(&self.guild_id)?.read();
+ let client = self.client.as_ref()?;
+ let cache = client.cache.try_borrow().ok()?;
+ let guild = cache.guild(self.guild_id)?;
+ let guild = guild.borrow();
let mut roles = self.roles
.iter()
.filter_map(|role_id| guild.roles.get(role_id))
- .collect::<Vec<&Role>>();
+ .collect::<Vec<&Rc<RefCell<Role>>>>();
roles.sort_by(|a, b| b.cmp(a));
let default = Colour::default();
roles
.iter()
- .find(|r| r.colour.0 != default.0)
- .map(|r| r.colour)
+ .find(|r| r.borrow().colour.0 != default.0)
+ .map(|r| r.borrow().colour)
}
/// Returns the "default channel" of the guild for the 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.find() {
- Some(guild) => guild,
- None => return None,
- };
-
- let reader = guild.read();
+ pub fn default_channel(&self) -> Option<Rc<RefCell<GuildChannel>>> {
+ let cache = self.client.as_ref()?.cache.borrow();
+ let guild = cache.guild(self.guild_id)?;
+ let reader = guild.borrow();
for (cid, channel) in &reader.channels {
- if reader.permissions_in(*cid, self.user.read().id).read_messages() {
- return Some(Arc::clone(channel));
+ if reader.permissions_in(*cid, self.user.borrow().id).read_messages() {
+ return Some(Rc::clone(channel));
}
}
@@ -205,17 +204,23 @@ impl Member {
self.nick
.as_ref()
.map(Cow::Borrowed)
- .unwrap_or_else(|| Cow::Owned(self.user.read().name.clone()))
+ .unwrap_or_else(|| {
+ Cow::Owned(unsafe { (*self.user.as_ptr()).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.read().discriminator
- )
+ unsafe {
+ let user = &*self.user.as_ptr();
+
+ format!(
+ "{}#{}",
+ self.display_name(),
+ user.discriminator,
+ )
+ }
}
/// Edits the member with the given data. See [`Guild::edit_member`] for
@@ -227,10 +232,13 @@ impl Member {
/// [`Guild::edit_member`]: ../model/guild/struct.Guild.html#method.edit_member
/// [`EditMember`]: ../builder/struct.EditMember.html
#[cfg(feature = "cache")]
- pub fn edit<F: FnOnce(EditMember) -> EditMember>(&self, f: F) -> Result<()> {
- let map = utils::vecmap_to_json_map(f(EditMember::default()).0);
-
- http::edit_member(self.guild_id.0, self.user.read().id.0, &map)
+ pub fn edit<F: FnOnce(EditMember) -> EditMember>(&self, f: F)
+ -> FutureResult<()> {
+ ftryopt!(self.client).http.edit_member(
+ self.guild_id.0,
+ self.user.borrow().id.0,
+ f,
+ )
}
/// Retrieves the ID and position of the member's highest role in the
@@ -249,13 +257,18 @@ impl Member {
/// guild.
#[cfg(feature = "cache")]
pub fn highest_role_info(&self) -> Option<(RoleId, i64)> {
- let guild = self.guild_id.find()?;
- let reader = guild.read();
+ let cache = self.client.as_ref()?.cache.try_borrow().ok()?;
+ let guild = cache.guild(self.guild_id)?;
+ let reader = guild.borrow();
let mut highest = None;
for role_id in &self.roles {
- if let Some(role) = reader.roles.get(&role_id) {
+ let role = reader.roles
+ .get(&role_id)
+ .and_then(|x| x.try_borrow().ok());
+
+ if let Some(role) = role {
// Skip this role if this role in iteration has:
//
// - a position less than the recorded highest
@@ -306,24 +319,30 @@ impl Member {
/// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [Kick Members]: permissions/constant.KICK_MEMBERS.html
- pub fn kick(&self) -> Result<()> {
+ pub fn kick(&self) -> FutureResult<()> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
- let cache = CACHE.read();
+ let cache = ftry!(client.cache.try_borrow());
if let Some(guild) = cache.guilds.get(&self.guild_id) {
let req = Permissions::KICK_MEMBERS;
- let reader = guild.read();
+ let reader = ftry!(guild.try_borrow());
if !reader.has_perms(req) {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
- reader.check_hierarchy(self.user.read().id)?;
+ ftry!(reader.check_hierarchy(ftry!(self.user.try_borrow()).id));
}
}
- self.guild_id.kick(self.user.read().id)
+ let user_id = ftry!(self.user.try_borrow()).id.0;
+
+ ftryopt!(self.client).http.kick_member(self.guild_id.0, user_id)
}
/// Returns the guild-level permissions for the member.
@@ -348,39 +367,39 @@ impl Member {
/// [`ModelError::ItemMissing`]: enum.ModelError.html#variant.ItemMissing
#[cfg(feature = "cache")]
pub fn permissions(&self) -> Result<Permissions> {
- let guild = match self.guild_id.find() {
+ let client = self.client.as_ref().ok_or_else(|| {
+ Error::Model(ModelError::ClientNotPresent)
+ })?;
+ let cache = client.cache.try_borrow()?;
+ let guild = match cache.guilds.get(&self.guild_id) {
Some(guild) => guild,
None => return Err(From::from(ModelError::GuildNotFound)),
};
- let reader = guild.read();
+ let guild = guild.try_borrow()?;
- Ok(reader.member_permissions(self.user.read().id))
+ Ok(guild.member_permissions(self.user.try_borrow()?.id))
}
- /// Removes a [`Role`] from the member, editing its roles in-place if the
- /// request was successful.
+ /// Removes a [`Role`] from the member.
///
/// **Note**: Requires the [Manage Roles] permission.
///
/// [`Role`]: struct.Role.html
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
#[cfg(feature = "cache")]
- pub fn remove_role<R: Into<RoleId>>(&mut self, role_id: R) -> Result<()> {
+ pub fn remove_role<R: Into<RoleId>>(&mut self, role_id: R) -> FutureResult<()> {
let role_id = role_id.into();
if !self.roles.contains(&role_id) {
- return Ok(());
+ return Box::new(future::ok(()));
}
- match http::remove_member_role(self.guild_id.0, self.user.read().id.0, role_id.0) {
- Ok(()) => {
- self.roles.retain(|r| r.0 != role_id.0);
-
- Ok(())
- },
- Err(why) => Err(why),
- }
+ ftryopt!(self.client).http.remove_member_role(
+ self.guild_id.0,
+ self.user.borrow().id.0,
+ role_id.0,
+ )
}
/// Removes one or multiple [`Role`]s from the member.
@@ -390,20 +409,15 @@ impl Member {
/// [`Role`]: struct.Role.html
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
#[cfg(feature = "cache")]
- pub fn remove_roles(&mut self, role_ids: &[RoleId]) -> Result<()> {
- self.roles.retain(|r| !role_ids.contains(r));
-
- let builder = EditMember::default().roles(&self.roles);
- let map = utils::vecmap_to_json_map(builder.0);
-
- match http::edit_member(self.guild_id.0, self.user.read().id.0, &map) {
- Ok(()) => Ok(()),
- Err(why) => {
- self.roles.extend_from_slice(role_ids);
+ pub fn remove_roles(&mut self, role_ids: &[RoleId]) -> FutureResult<()> {
+ let mut roles = self.roles.clone();
+ roles.retain(|r| !role_ids.contains(r));
- Err(why)
- },
- }
+ ftryopt!(self.client).http.edit_member(
+ self.guild_id.0,
+ self.user.borrow().id.0,
+ |f| f.roles(roles),
+ )
}
/// Retrieves the full role data for the user's roles.
@@ -412,17 +426,29 @@ 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>> {
- self
- .guild_id
- .find()
- .map(|g| g
- .read()
+ pub fn roles(&self) -> Option<Vec<Rc<RefCell<Role>>>> {
+ let client = self.client.as_ref()?;
+ let cache = client.cache.try_borrow().ok()?;
+
+ cache.guilds.get(&self.guild_id).and_then(|guild| {
+ let guild = guild.try_borrow().ok()?;
+
+ let roles = guild
.roles
.values()
- .filter(|role| self.roles.contains(&role.id))
+ .filter(|role| {
+ let role = match role.try_borrow() {
+ Ok(role) => role,
+ Err(_) => return false,
+ };
+
+ self.roles.contains(&role.id)
+ })
.cloned()
- .collect())
+ .collect();
+
+ Some(roles)
+ })
}
/// Unbans the [`User`] from the guild.
@@ -438,23 +464,10 @@ impl Member {
/// [`User`]: struct.User.html
/// [Ban Members]: permissions/constant.BAN_MEMBERS.html
#[cfg(feature = "cache")]
- pub fn unban(&self) -> Result<()> {
- http::remove_ban(self.guild_id.0, self.user.read().id.0)
- }
-}
-
-impl Display for Member {
- /// Mentions the user so that they receive a notification.
- ///
- /// # Examples
- ///
- /// ```rust,ignore
- /// // assumes a `member` has already been bound
- /// println!("{} is a member!", member);
- /// ```
- ///
- // This is in the format of `<@USER_ID>`.
- fn fmt(&self, f: &mut Formatter) -> FmtResult {
- Display::fmt(&self.user.read().mention(), f)
+ pub fn unban(&self) -> FutureResult<()> {
+ ftryopt!(self.client).http.remove_ban(
+ self.guild_id.0,
+ self.user.borrow().id.0,
+ )
}
}
diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs
index 142fc73..6c0fa82 100644
--- a/src/model/guild/mod.rs
+++ b/src/model/guild/mod.rs
@@ -17,15 +17,16 @@ pub use self::role::*;
pub use self::audit_log::*;
use chrono::{DateTime, FixedOffset};
+use futures::{Future, future};
use model::prelude::*;
use serde::de::Error as DeError;
use serde_json;
+use std::cell::RefCell;
+use std::rc::Rc;
use super::utils::*;
+use super::WrappedClient;
+use ::FutureResult;
-#[cfg(all(feature = "cache", feature = "model"))]
-use CACHE;
-#[cfg(feature = "model")]
-use http;
#[cfg(feature = "model")]
use builder::{EditGuild, EditMember, EditRole};
#[cfg(feature = "model")]
@@ -58,8 +59,8 @@ pub struct Guild {
///
/// This contains all channels regardless of permissions (i.e. the ability
/// of the bot to read from or connect to them).
- #[serde(serialize_with = "serialize_gen_locked_map")]
- pub channels: HashMap<ChannelId, Arc<RwLock<GuildChannel>>>,
+ #[serde(serialize_with = "serialize_gen_rc_map")]
+ pub channels: HashMap<ChannelId, Rc<RefCell<GuildChannel>>>,
/// Indicator of whether notifications for all messages are enabled by
/// default in the guild.
pub default_message_notifications: DefaultMessageNotificationLevel,
@@ -102,8 +103,8 @@ pub struct Guild {
/// the library.
///
/// [`ReadyEvent`]: events/struct.ReadyEvent.html
- #[serde(serialize_with = "serialize_gen_map")]
- pub members: HashMap<UserId, Member>,
+ #[serde(serialize_with = "serialize_gen_rc_map")]
+ pub members: HashMap<UserId, Rc<RefCell<Member>>>,
/// Indicator of whether the guild requires multi-factor authentication for
/// [`Role`]s or [`User`]s with moderation permissions.
///
@@ -119,13 +120,13 @@ pub struct Guild {
/// A mapping of [`User`]s' Ids to their current presences.
///
/// [`User`]: struct.User.html
- #[serde(serialize_with = "serialize_gen_map")]
- pub presences: HashMap<UserId, Presence>,
+ #[serde(serialize_with = "serialize_gen_rc_map")]
+ pub presences: HashMap<UserId, Rc<RefCell<Presence>>>,
/// The region that the voice servers that the guild uses are located in.
pub region: String,
/// A mapping of the guild's roles.
- #[serde(serialize_with = "serialize_gen_map")]
- pub roles: HashMap<RoleId, Role>,
+ #[serde(serialize_with = "serialize_gen_rc_map")]
+ pub roles: HashMap<RoleId, Rc<RefCell<Role>>>,
/// An identifying hash of the guild's splash icon.
///
/// If the [`InviteSplash`] feature is enabled, this can be used to generate
@@ -142,13 +143,19 @@ pub struct Guild {
/// [`User`]: struct.User.html
#[serde(serialize_with = "serialize_gen_map")]
pub voice_states: HashMap<UserId, VoiceState>,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
impl Guild {
#[cfg(feature = "cache")]
fn check_hierarchy(&self, other_user: UserId) -> Result<()> {
- let current_id = CACHE.read().user.id;
+ let client = self.client.as_ref().ok_or_else(|| {
+ Error::Model(ModelError::ClientNotPresent)
+ })?;
+
+ let current_id = client.cache.try_borrow()?.user.id;
if let Some(higher) = self.greater_member_hierarchy(other_user, current_id) {
if higher != current_id {
@@ -162,10 +169,10 @@ impl Guild {
/// Returns the "default" channel of the guild for the passed user id.
/// (This returns the first channel that can be read by the user, if there isn't one,
/// returns `None`)
- pub fn default_channel(&self, uid: UserId) -> Option<Arc<RwLock<GuildChannel>>> {
+ pub fn default_channel(&self, uid: UserId) -> Option<Rc<RefCell<GuildChannel>>> {
for (cid, channel) in &self.channels {
if self.permissions_in(*cid, uid).read_messages() {
- return Some(Arc::clone(channel));
+ return Some(Rc::clone(channel));
}
}
@@ -177,11 +184,11 @@ impl Guild {
/// returns `None`)
/// Note however that this is very costy if used in a server with lots of channels,
/// members, or both.
- pub fn default_channel_guaranteed(&self) -> Option<Arc<RwLock<GuildChannel>>> {
+ pub fn default_channel_guaranteed(&self) -> Option<Rc<RefCell<GuildChannel>>> {
for (cid, channel) in &self.channels {
for memid in self.members.keys() {
if self.permissions_in(*cid, *memid).read_messages() {
- return Some(Arc::clone(channel));
+ return Some(Rc::clone(channel));
}
}
}
@@ -191,7 +198,16 @@ impl Guild {
#[cfg(feature = "cache")]
fn has_perms(&self, mut permissions: Permissions) -> bool {
- let user_id = CACHE.read().user.id;
+ let client = match self.client.as_ref() {
+ Some(client) => client,
+ None => return true,
+ };
+ let cache = match client.cache.try_borrow() {
+ Ok(cache) => cache,
+ Err(_) => return true,
+ };
+
+ let user_id = cache.user.id;
let perms = self.member_permissions(user_id);
permissions.remove(perms);
@@ -229,7 +245,8 @@ impl Guild {
/// [`Guild::ban`]: struct.Guild.html#method.ban
/// [`User`]: struct.User.html
/// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- pub fn ban<U: Into<UserId>, BO: BanOptions>(&self, user: U, options: &BO) -> Result<()> {
+ pub fn ban<U: Into<UserId>, BO: BanOptions>(&self, user: U, options: &BO)
+ -> FutureResult<()> {
let user = user.into();
#[cfg(feature = "cache")]
@@ -237,13 +254,33 @@ impl Guild {
let req = Permissions::BAN_MEMBERS;
if !self.has_perms(req) {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
- self.check_hierarchy(user)?;
+ ftry!(self.check_hierarchy(user));
+ }
+
+ let dmd = options.dmd();
+ if dmd > 7 {
+ return Box::new(future::err(Error::Model(
+ ModelError::DeleteMessageDaysAmount(dmd),
+ )));
}
- self.id.ban(user, options)
+ let reason = options.reason();
+
+ if reason.len() > 512 {
+ return Box::new(future::err(Error::ExceededLimit(
+ reason.to_string(),
+ 512,
+ )));
+ }
+
+ ftryopt!(self.client)
+ .http
+ .ban_user(self.id.0, user.0, dmd, reason)
}
/// Retrieves a list of [`Ban`]s for the guild.
@@ -258,69 +295,60 @@ impl Guild {
/// [`Ban`]: struct.Ban.html
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- pub fn bans(&self) -> Result<Vec<Ban>> {
+ pub fn bans(&self) -> FutureResult<Vec<Ban>> {
#[cfg(feature = "cache")]
{
let req = Permissions::BAN_MEMBERS;
if !self.has_perms(req) {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.id.bans()
+ ftryopt!(self.client).http.get_bans(self.id.0)
}
/// Retrieves a list of [`AuditLogs`] for the guild.
///
/// [`AuditLogs`]: audit_log/struct.AuditLogs.html
#[inline]
- pub fn audit_logs(&self, action_type: Option<u8>,
- user_id: Option<UserId>,
- before: Option<AuditLogEntryId>,
- limit: Option<u8>) -> Result<AuditLogs> {
- self.id.audit_logs(action_type, user_id, before, limit)
+ pub fn audit_logs(
+ &self,
+ action_type: Option<u8>,
+ user_id: Option<UserId>,
+ before: Option<AuditLogEntryId>,
+ limit: Option<u8>,
+ ) -> FutureResult<AuditLogs> {
+ ftryopt!(self.client).http.get_audit_logs(
+ self.id.0,
+ action_type,
+ user_id.map(|x| x.0),
+ before.map(|x| x.0),
+ limit,
+ )
}
/// Gets all of the guild's channels over the REST API.
///
/// [`Guild`]: struct.Guild.html
#[inline]
- pub fn channels(&self) -> Result<HashMap<ChannelId, GuildChannel>> { self.id.channels() }
+ pub fn channels(&self) -> FutureResult<HashMap<ChannelId, GuildChannel>> {
+ let done = ftryopt!(self.client)
+ .http
+ .get_channels(self.id.0)
+ .map(|channels| {
+ let mut map = HashMap::with_capacity(channels.len());
+
+ for channel in channels {
+ map.insert(channel.id, channel);
+ }
- /// Creates a guild with the data provided.
- ///
- /// Only a [`PartialGuild`] will be immediately returned, and a full
- /// [`Guild`] will be received over a [`Shard`].
- ///
- /// **Note**: This endpoint is usually only available for user accounts.
- /// Refer to Discord's information for the endpoint [here][whitelist] for
- /// more information. If you require this as a bot, re-think what you are
- /// doing and if it _really_ needs to be doing this.
- ///
- /// # Examples
- ///
- /// Create a guild called `"test"` in the [US West region] with no icon:
- ///
- /// ```rust,ignore
- /// use serenity::model::{Guild, Region};
- ///
- /// let _guild = Guild::create_guild("test", Region::UsWest, None);
- /// ```
- ///
- /// [`Guild`]: struct.Guild.html
- /// [`PartialGuild`]: struct.PartialGuild.html
- /// [`Shard`]: ../gateway/struct.Shard.html
- /// [US West region]: enum.Region.html#variant.UsWest
- /// [whitelist]: https://discordapp.com/developers/docs/resources/guild#create-guild
- pub fn create(name: &str, region: Region, icon: Option<&str>) -> Result<PartialGuild> {
- let map = json!({
- "icon": icon,
- "name": name,
- "region": region.name(),
- });
-
- http::create_guild(&map)
+ map
+ });
+
+ Box::new(done)
}
/// Creates a new [`Channel`] in the guild.
@@ -345,18 +373,25 @@ impl Guild {
/// [`Channel`]: struct.Channel.html
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
- pub fn create_channel<C>(&self, name: &str, kind: ChannelType, category: C) -> Result<GuildChannel>
- where C: Into<Option<ChannelId>> {
+ pub fn create_channel<C>(&self, name: &str, kind: ChannelType, category: C)
+ -> FutureResult<GuildChannel> where C: Into<Option<ChannelId>> {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_CHANNELS;
if !self.has_perms(req) {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.id.create_channel(name, kind, category)
+ ftryopt!(self.client).http.create_channel(
+ self.id.0,
+ name,
+ kind,
+ category.into().map(|x| x.0),
+ )
}
/// Creates an emoji in the guild with a name and base64-encoded image. The
@@ -379,8 +414,8 @@ impl Guild {
/// [`utils::read_image`]: ../fn.read_image.html
/// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
#[inline]
- pub fn create_emoji(&self, name: &str, image: &str) -> Result<Emoji> {
- self.id.create_emoji(name, image)
+ pub fn create_emoji(&self, name: &str, image: &str) -> FutureResult<Emoji> {
+ ftryopt!(self.client).http.create_emoji(self.id.0, name, image)
}
/// Creates an integration for the guild.
@@ -389,9 +424,13 @@ impl Guild {
///
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
#[inline]
- pub fn create_integration<I>(&self, integration_id: I, kind: &str) -> Result<()>
- where I: Into<IntegrationId> {
- self.id.create_integration(integration_id, kind)
+ pub fn create_integration<I>(&self, integration_id: I, kind: &str)
+ -> FutureResult<()> where I: Into<IntegrationId> {
+ ftryopt!(self.client).http.create_guild_integration(
+ self.id.0,
+ integration_id.into().0,
+ kind,
+ )
}
/// Creates a new role in the guild with the data set, if any.
@@ -416,18 +455,20 @@ impl Guild {
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [`Role`]: struct.Role.html
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
- pub fn create_role<F>(&self, f: F) -> Result<Role>
+ pub fn create_role<F>(&self, f: F) -> FutureResult<Role>
where F: FnOnce(EditRole) -> EditRole {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_ROLES;
if !self.has_perms(req) {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.id.create_role(f)
+ ftryopt!(self.client).http.create_role(self.id.0, f)
}
/// Deletes the current guild if the current user is the owner of the
@@ -441,17 +482,23 @@ impl Guild {
/// if the current user is not the guild owner.
///
/// [`ModelError::InvalidUser`]: enum.ModelError.html#variant.InvalidUser
- pub fn delete(&self) -> Result<PartialGuild> {
+ pub fn delete(&self) -> FutureResult<PartialGuild> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
- if self.owner_id != CACHE.read().user.id {
+ let cache = ftry!(client.cache.try_borrow());
+
+ if self.owner_id != cache.user.id {
let req = Permissions::MANAGE_GUILD;
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.id.delete()
+ ftryopt!(self.client).http.delete_guild(self.id.0)
}
/// Deletes an [`Emoji`] from the guild.
@@ -461,8 +508,9 @@ impl Guild {
/// [`Emoji`]: struct.Emoji.html
/// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
#[inline]
- pub fn delete_emoji<E: Into<EmojiId>>(&self, emoji_id: E) -> Result<()> {
- self.id.delete_emoji(emoji_id)
+ pub fn delete_emoji<E: Into<EmojiId>>(&self, emoji_id: E)
+ -> FutureResult<()> {
+ ftryopt!(self.client).http.delete_emoji(self.id.0, emoji_id.into().0)
}
/// Deletes an integration by Id from the guild.
@@ -471,8 +519,12 @@ impl Guild {
///
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
#[inline]
- pub fn delete_integration<I: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
- self.id.delete_integration(integration_id)
+ pub fn delete_integration<I: Into<IntegrationId>>(&self, integration_id: I)
+ -> FutureResult<()> {
+ ftryopt!(self.client).http.delete_guild_integration(
+ self.id.0,
+ integration_id.into().0,
+ )
}
/// Deletes a [`Role`] by Id from the guild.
@@ -486,8 +538,8 @@ impl Guild {
/// [`Role::delete`]: struct.Role.html#method.delete
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
#[inline]
- pub fn delete_role<R: Into<RoleId>>(&self, role_id: R) -> Result<()> {
- self.id.delete_role(role_id)
+ pub fn delete_role<R: Into<RoleId>>(&self, role_id: R) -> FutureResult<()> {
+ ftryopt!(self.client).http.delete_role(self.id.0, role_id.into().0)
}
/// Edits the current guild with new data where specified.
@@ -518,37 +570,20 @@ impl Guild {
///
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- pub fn edit<F>(&mut self, f: F) -> Result<()>
- where F: FnOnce(EditGuild) -> EditGuild {
+ pub fn edit<F: FnOnce(EditGuild) -> EditGuild>(&self, f: F)
+ -> FutureResult<PartialGuild> {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_GUILD;
if !self.has_perms(req) {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- match self.id.edit(f) {
- Ok(guild) => {
- self.afk_channel_id = guild.afk_channel_id;
- self.afk_timeout = guild.afk_timeout;
- self.default_message_notifications = guild.default_message_notifications;
- self.emojis = guild.emojis;
- self.features = guild.features;
- self.icon = guild.icon;
- self.mfa_level = guild.mfa_level;
- self.name = guild.name;
- self.owner_id = guild.owner_id;
- self.region = guild.region;
- self.roles = guild.roles;
- self.splash = guild.splash;
- self.verification_level = guild.verification_level;
-
- Ok(())
- },
- Err(why) => Err(why),
- }
+ ftryopt!(self.client).http.edit_guild(self.id.0, f)
}
/// Edits an [`Emoji`]'s name in the guild.
@@ -562,8 +597,11 @@ impl Guild {
/// [`Emoji::edit`]: struct.Emoji.html#method.edit
/// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
#[inline]
- pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str) -> Result<Emoji> {
- self.id.edit_emoji(emoji_id, name)
+ pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str)
+ -> FutureResult<Emoji> {
+ let emoji_id = emoji_id.into().0;
+
+ ftryopt!(self.client).http.edit_emoji(self.id.0, emoji_id, name)
}
/// Edits the properties of member of the guild, such as muting or
@@ -580,9 +618,9 @@ impl Guild {
/// guild.edit_member(user_id, |m| m.mute(true).roles(&vec![role_id]));
/// ```
#[inline]
- pub fn edit_member<F, U>(&self, user_id: U, f: F) -> Result<()>
+ pub fn edit_member<F, U>(&self, user_id: U, f: F) -> FutureResult<()>
where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> {
- self.id.edit_member(user_id, f)
+ ftryopt!(self.client).http.edit_member(self.id.0, user_id.into().0, f)
}
/// Edits the current user's nickname for the guild.
@@ -599,17 +637,20 @@ impl Guild {
///
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html
- pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
+ pub fn edit_nickname(&self, new_nickname: Option<&str>)
+ -> FutureResult<()> {
#[cfg(feature = "cache")]
{
let req = Permissions::CHANGE_NICKNAME;
if !self.has_perms(req) {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.id.edit_nickname(new_nickname)
+ ftryopt!(self.client).http.edit_nickname(self.id.0, new_nickname)
}
/// Edits a role, optionally setting its fields.
@@ -626,9 +667,9 @@ impl Guild {
///
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
#[inline]
- pub fn edit_role<F, R>(&self, role_id: R, f: F) -> Result<Role>
+ pub fn edit_role<F, R>(&self, role_id: R, f: F) -> FutureResult<Role>
where F: FnOnce(EditRole) -> EditRole, R: Into<RoleId> {
- self.id.edit_role(role_id, f)
+ ftryopt!(self.client).http.edit_role(self.id.0, role_id.into().0, f)
}
/// Edits the order of [`Role`]s
@@ -646,17 +687,13 @@ impl Guild {
/// [`Role`]: struct.Role.html
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
#[inline]
- pub fn edit_role_position<R>(&self, role_id: R, position: u64) -> Result<Vec<Role>>
- where R: Into<RoleId> {
- self.id.edit_role_position(role_id, position)
+ pub fn edit_role_position<R>(&self, role_id: R, position: u64)
+ -> FutureResult<Vec<Role>> where R: Into<RoleId> {
+ ftryopt!(self.client)
+ .http
+ .edit_role_position(self.id.0, role_id.into().0, position)
}
- /// Gets a partial amount of guild data by its Id.
- ///
- /// Requires that the current user be in the guild.
- #[inline]
- pub fn get<G: Into<GuildId>>(guild_id: G) -> Result<PartialGuild> { guild_id.into().get() }
-
/// Returns which of two [`User`]s has a higher [`Member`] hierarchy.
///
/// Hierarchy is essentially who has the [`Role`] with the highest
@@ -687,9 +724,11 @@ impl Guild {
}
let lhs = self.members.get(&lhs_id)?
+ .borrow()
.highest_role_info()
.unwrap_or((RoleId(0), 0));
let rhs = self.members.get(&rhs_id)?
+ .borrow()
.highest_role_info()
.unwrap_or((RoleId(0), 0));
@@ -731,7 +770,9 @@ impl Guild {
///
/// This performs a request over the REST API.
#[inline]
- pub fn integrations(&self) -> Result<Vec<Integration>> { self.id.integrations() }
+ pub fn integrations(&self) -> FutureResult<Vec<Integration>> {
+ ftryopt!(self.client).http.get_guild_integrations(self.id.0)
+ }
/// Retrieves the active invites for the guild.
///
@@ -744,23 +785,27 @@ impl Guild {
///
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- pub fn invites(&self) -> Result<Vec<RichInvite>> {
+ pub fn invites(&self) -> FutureResult<Vec<RichInvite>> {
#[cfg(feature = "cache")]
{
let req = Permissions::MANAGE_GUILD;
if !self.has_perms(req) {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.id.invites()
+ ftryopt!(self.client).http.get_guild_invites(self.id.0)
}
/// Checks if the guild is 'large'. A guild is considered large if it has
/// more than 250 members.
#[inline]
- pub fn is_large(&self) -> bool { self.members.len() > LARGE_THRESHOLD as usize }
+ pub fn is_large(&self) -> bool {
+ self.members.len() > LARGE_THRESHOLD as usize
+ }
/// Kicks a [`Member`] from the guild.
///
@@ -769,18 +814,24 @@ impl Guild {
/// [`Member`]: struct.Member.html
/// [Kick Members]: permissions/constant.KICK_MEMBERS.html
#[inline]
- pub fn kick<U: Into<UserId>>(&self, user_id: U) -> Result<()> { self.id.kick(user_id) }
+ pub fn kick<U: Into<UserId>>(&self, user_id: U) -> FutureResult<()> {
+ ftryopt!(self.client).http.kick_member(self.id.0, user_id.into().0)
+ }
/// Leaves the guild.
#[inline]
- pub fn leave(&self) -> Result<()> { self.id.leave() }
+ pub fn leave(&self) -> FutureResult<()> {
+ ftryopt!(self.client).http.leave_guild(self.id.0)
+ }
/// Gets a user's [`Member`] for the guild by Id.
///
/// [`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, user_id: U) -> FutureResult<Member> {
+ ftryopt!(self.client).http.get_member(self.id.0, user_id.into().0)
+ }
/// Gets a list of the guild's members.
///
@@ -790,18 +841,21 @@ impl Guild {
///
/// [`User`]: struct.User.html
#[inline]
- pub fn members<U>(&self, limit: Option<u64>, after: Option<U>) -> Result<Vec<Member>>
- where U: Into<UserId> {
- self.id.members(limit, after)
+ pub fn members<U>(&self, limit: Option<u64>, after: Option<U>)
+ -> FutureResult<Vec<Member>> where U: Into<UserId> {
+ let after = after.map(Into::into).map(|x| x.0);
+
+ ftryopt!(self.client).http.get_guild_members(self.id.0, limit, after)
}
/// Gets a list of all the members (satisfying the status provided to the function) in this
/// guild.
- pub fn members_with_status(&self, status: OnlineStatus) -> Vec<&Member> {
+ pub fn members_with_status(&self, status: OnlineStatus)
+ -> Vec<&Rc<RefCell<Member>>> {
let mut members = vec![];
for (&id, member) in &self.members {
- match self.presences.get(&id) {
+ match self.presences.get(&id).and_then(|x| x.try_borrow().ok()) {
Some(presence) => if status == presence.status {
members.push(member);
},
@@ -828,7 +882,7 @@ impl Guild {
/// - **username and discriminator**: "zey#5479"
///
/// [`Member`]: struct.Member.html
- pub fn member_named(&self, name: &str) -> Option<&Member> {
+ pub fn member_named(&self, name: &str) -> Option<&Rc<RefCell<Member>>> {
let (name, discrim) = if let Some(pos) = name.rfind('#') {
let split = name.split_at(pos + 1);
@@ -851,9 +905,18 @@ impl Guild {
self.members
.values()
.find(|member| {
- let name_matches = member.user.read().name == name;
+ let member = match member.try_borrow().ok() {
+ Some(member) => member,
+ None => return false,
+ };
+ let user = match member.user.try_borrow().ok() {
+ Some(user) => user,
+ None => return false,
+ };
+
+ let name_matches = user.name == name;
let discrim_matches = match discrim {
- Some(discrim) => member.user.read().discriminator == discrim,
+ Some(discrim) => user.discriminator == discrim,
None => true,
};
@@ -862,7 +925,7 @@ impl Guild {
.or_else(|| {
self.members
.values()
- .find(|member| member.nick.as_ref().map_or(false, |nick| nick == name))
+ .find(|member| member.borrow().nick.as_ref().map_or(false, |nick| nick == name))
})
}
@@ -876,18 +939,23 @@ impl Guild {
/// - "zeya", "zeyaa", "zeyla", "zeyzey", "zeyzeyzey"
///
/// [`Member`]: struct.Member.html
- pub fn members_starting_with(&self, prefix: &str, case_sensitive: bool, sorted: bool) -> Vec<&Member> {
- let mut members: Vec<&Member> = self.members
+ pub fn members_starting_with(&self, prefix: &str, case_sensitive: bool, sorted: bool) -> Vec<&Rc<RefCell<Member>>> {
+ let mut members: Vec<&Rc<RefCell<Member>>> = self.members
.values()
.filter(|member|
-
if case_sensitive {
- member.user.read().name.starts_with(prefix)
+ let member = member.borrow();
+ let user = member.user.borrow();
+
+ user.name.starts_with(prefix)
} else {
- starts_with_case_insensitive(&member.user.read().name, prefix)
+ let member = member.borrow();
+ let user = member.user.borrow();
+
+ starts_with_case_insensitive(&user.name, prefix)
}
- || member.nick.as_ref()
+ || member.borrow().nick.as_ref()
.map_or(false, |nick|
if case_sensitive {
@@ -899,26 +967,28 @@ impl Guild {
if sorted {
members
.sort_by(|a, b| {
+ let (a, b) = (a.borrow(), b.borrow());
+
let name_a = match a.nick {
Some(ref nick) => {
- if contains_case_insensitive(&a.user.read().name[..], prefix) {
- Cow::Owned(a.user.read().name.clone())
+ if contains_case_insensitive(&a.user.borrow().name[..], prefix) {
+ Cow::Owned(a.user.borrow().name.clone())
} else {
Cow::Borrowed(nick)
}
},
- None => Cow::Owned(a.user.read().name.clone()),
+ None => Cow::Owned(a.user.borrow().name.clone()),
};
let name_b = match b.nick {
Some(ref nick) => {
- if contains_case_insensitive(&b.user.read().name[..], prefix) {
- Cow::Owned(b.user.read().name.clone())
+ if contains_case_insensitive(&b.user.borrow().name[..], prefix) {
+ Cow::Owned(b.user.borrow().name.clone())
} else {
Cow::Borrowed(nick)
}
},
- None => Cow::Owned(b.user.read().name.clone()),
+ None => Cow::Owned(b.user.borrow().name.clone()),
};
closest_to_origin(prefix, &name_a[..], &name_b[..])
@@ -951,18 +1021,24 @@ impl Guild {
/// as both fields have to be considered again for sorting.
///
/// [`Member`]: struct.Member.html
- pub fn members_containing(&self, substring: &str, case_sensitive: bool, sorted: bool) -> Vec<&Member> {
- let mut members: Vec<&Member> = self.members
+ pub fn members_containing(&self, substring: &str, case_sensitive: bool, sorted: bool) -> Vec<&Rc<RefCell<Member>>> {
+ let mut members: Vec<&Rc<RefCell<Member>>> = self.members
.values()
.filter(|member|
if case_sensitive {
- member.user.read().name.contains(substring)
+ let member = member.borrow();
+ let user = member.user.borrow();
+
+ user.name.contains(substring)
} else {
- contains_case_insensitive(&member.user.read().name, substring)
+ let member = member.borrow();
+ let user = member.user.borrow();
+
+ contains_case_insensitive(&user.name, substring)
}
- || member.nick.as_ref()
+ || member.borrow().nick.as_ref()
.map_or(false, |nick| {
if case_sensitive {
@@ -975,26 +1051,28 @@ impl Guild {
if sorted {
members
.sort_by(|a, b| {
+ let (a, b) = (a.borrow(), b.borrow());
+
let name_a = match a.nick {
Some(ref nick) => {
- if contains_case_insensitive(&a.user.read().name[..], substring) {
- Cow::Owned(a.user.read().name.clone())
+ if contains_case_insensitive(&a.user.borrow().name[..], substring) {
+ Cow::Owned(a.user.borrow().name.clone())
} else {
Cow::Borrowed(nick)
}
},
- None => Cow::Owned(a.user.read().name.clone()),
+ None => Cow::Owned(a.user.borrow().name.clone()),
};
let name_b = match b.nick {
Some(ref nick) => {
- if contains_case_insensitive(&b.user.read().name[..], substring) {
- Cow::Owned(b.user.read().name.clone())
+ if contains_case_insensitive(&b.user.borrow().name[..], substring) {
+ Cow::Owned(b.user.borrow().name.clone())
} else {
Cow::Borrowed(nick)
}
},
- None => Cow::Owned(b.user.read().name.clone()),
+ None => Cow::Owned(b.user.borrow().name.clone()),
};
closest_to_origin(substring, &name_a[..], &name_b[..])
@@ -1021,22 +1099,34 @@ impl Guild {
/// - "zey", "azey", "zeyla", "zeylaa", "zeyzeyzey"
///
/// [`Member`]: struct.Member.html
- pub fn members_username_containing(&self, substring: &str, case_sensitive: bool, sorted: bool) -> Vec<&Member> {
- let mut members: Vec<&Member> = self.members
+ pub fn members_username_containing(&self, substring: &str, case_sensitive: bool, sorted: bool) -> Vec<&Rc<RefCell<Member>>> {
+ let mut members: Vec<&Rc<RefCell<Member>>> = self.members
.values()
.filter(|member| {
+ let member = match member.try_borrow().ok() {
+ Some(member) => member,
+ None => return false,
+ };
+ let user = match member.user.try_borrow().ok() {
+ Some(user) => user,
+ None => return false,
+ };
+
if case_sensitive {
- member.user.read().name.contains(substring)
+ user.name.contains(substring)
} else {
- contains_case_insensitive(&member.user.read().name, substring)
+ contains_case_insensitive(&user.name, substring)
}
}).collect();
if sorted {
members
.sort_by(|a, b| {
- let name_a = &a.user.read().name;
- let name_b = &b.user.read().name;
+ let (a, b) = (a.borrow(), b.borrow());
+ let (a_user, b_user) = (a.user.borrow(), b.user.borrow());
+
+ let name_a = &a_user.name;
+ let name_b = &b_user.name;
closest_to_origin(substring, &name_a[..], &name_b[..])
});
members
@@ -1064,35 +1154,42 @@ impl Guild {
/// a nick, the username will be used (this should never happen).
///
/// [`Member`]: struct.Member.html
- pub fn members_nick_containing(&self, substring: &str, case_sensitive: bool, sorted: bool) -> Vec<&Member> {
- let mut members: Vec<&Member> = self.members
+ pub fn members_nick_containing(&self, substring: &str, case_sensitive: bool, sorted: bool) -> Vec<&Rc<RefCell<Member>>> {
+ let mut members = self.members
.values()
- .filter(|member|
+ .filter(|member| {
+ let member = match member.try_borrow() {
+ Ok(member) => member,
+ Err(_) => return false,
+ };
+
member.nick.as_ref()
.map_or(false, |nick| {
-
if case_sensitive {
nick.contains(substring)
} else {
contains_case_insensitive(nick, substring)
}
- })).collect();
+ })
+ }).collect::<Vec<&Rc<RefCell<Member>>>>();
if sorted {
members
.sort_by(|a, b| {
+ let (a, b) = (a.borrow(), b.borrow());
+
let name_a = match a.nick {
Some(ref nick) => {
Cow::Borrowed(nick)
},
- None => Cow::Owned(a.user.read().name.clone()),
+ None => Cow::Owned(a.user.borrow().name.clone()),
};
let name_b = match b.nick {
Some(ref nick) => {
Cow::Borrowed(nick)
},
- None => Cow::Owned(b.user.read().name.clone()),
+ None => Cow::Owned(b.user.borrow().name.clone()),
};
closest_to_origin(substring, &name_a[..], &name_b[..])
@@ -1114,7 +1211,7 @@ impl Guild {
return Permissions::all();
}
- let everyone = match self.roles.get(&RoleId(self.id.0)) {
+ let everyone = match self.roles.get(&RoleId(self.id.0)).and_then(|x| x.try_borrow().ok()) {
Some(everyone) => everyone,
None => {
error!(
@@ -1127,7 +1224,7 @@ impl Guild {
},
};
- let member = match self.members.get(&user_id) {
+ let member = match self.members.get(&user_id).and_then(|x| x.try_borrow().ok()) {
Some(member) => member,
None => return everyone.permissions,
};
@@ -1135,7 +1232,7 @@ impl Guild {
let mut permissions = everyone.permissions;
for role in &member.roles {
- if let Some(role) = self.roles.get(role) {
+ if let Some(role) = self.roles.get(role).and_then(|x| x.try_borrow().ok()) {
if role.permissions.contains(Permissions::ADMINISTRATOR) {
return Permissions::all();
}
@@ -1144,7 +1241,7 @@ impl Guild {
} else {
warn!(
"(╯°□°)╯︵ ┻━┻ {} on {} has non-existent role {:?}",
- member.user.read().id,
+ member.user.borrow().id,
self.id,
role,
);
@@ -1160,9 +1257,13 @@ impl Guild {
///
/// [Move Members]: permissions/constant.MOVE_MEMBERS.html
#[inline]
- pub fn move_member<C, U>(&self, user_id: U, channel_id: C) -> Result<()>
- where C: Into<ChannelId>, U: Into<UserId> {
- self.id.move_member(user_id, channel_id)
+ pub fn move_member<C, U>(&self, user_id: U, channel_id: C)
+ -> FutureResult<()> where C: Into<ChannelId>, U: Into<UserId> {
+ ftryopt!(self.client).http.edit_member(
+ self.id.0,
+ user_id.into().0,
+ |f| f.voice_channel(channel_id.into().0),
+ )
}
/// Alias for [`permissions_in`].
@@ -1191,7 +1292,7 @@ impl Guild {
let channel_id = channel_id.into();
// Start by retrieving the @everyone role's permissions.
- let everyone = match self.roles.get(&RoleId(self.id.0)) {
+ let everyone = match self.roles.get(&RoleId(self.id.0)).and_then(|x| x.try_borrow().ok()) {
Some(everyone) => everyone,
None => {
error!(
@@ -1207,18 +1308,18 @@ impl Guild {
// Create a base set of permissions, starting with `@everyone`s.
let mut permissions = everyone.permissions;
- let member = match self.members.get(&user_id) {
+ let member = match self.members.get(&user_id).and_then(|x| x.try_borrow().ok()) {
Some(member) => member,
None => return everyone.permissions,
};
for &role in &member.roles {
- if let Some(role) = self.roles.get(&role) {
+ if let Some(role) = self.roles.get(&role).and_then(|x| x.try_borrow().ok()) {
permissions |= role.permissions;
} else {
warn!(
"(╯°□°)╯︵ ┻━┻ {} on {} has non-existent role {:?}",
- member.user.read().id,
+ member.user.borrow().id,
self.id,
role
);
@@ -1231,7 +1332,7 @@ impl Guild {
}
if let Some(channel) = self.channels.get(&channel_id) {
- let channel = channel.read();
+ let channel = channel.borrow();
// If this is a text channel, then throw out voice permissions.
if channel.kind == ChannelType::Text {
@@ -1259,7 +1360,7 @@ impl Guild {
continue;
}
- if let Some(role) = self.roles.get(&role) {
+ if let Some(role) = self.roles.get(&role).and_then(|x| x.try_borrow().ok()) {
data.push((role.position, overwrite.deny, overwrite.allow));
}
}
@@ -1332,17 +1433,19 @@ impl Guild {
/// [`GuildPrune`]: struct.GuildPrune.html
/// [`Member`]: struct.Member.html
/// [Kick Members]: permissions/constant.KICK_MEMBERS.html
- pub fn prune_count(&self, days: u16) -> Result<GuildPrune> {
+ pub fn prune_count(&self, days: u16) -> FutureResult<GuildPrune> {
#[cfg(feature = "cache")]
{
let req = Permissions::KICK_MEMBERS;
if !self.has_perms(req) {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.id.prune_count(days)
+ ftryopt!(self.client).http.get_guild_prune_count(self.id.0, days)
}
/// Re-orders the channels of the guild.
@@ -1350,9 +1453,12 @@ impl Guild {
/// Although not required, you should specify all channels' positions,
/// regardless of whether they were updated. Otherwise, positioning can
/// sometimes get weird.
- pub fn reorder_channels<It>(&self, channels: It) -> Result<()>
+ pub fn reorder_channels<It>(&self, channels: It) -> FutureResult<()>
where It: IntoIterator<Item = (ChannelId, u64)> {
- self.id.reorder_channels(channels)
+ ftryopt!(self.client).http.edit_guild_channel_positions(
+ self.id.0,
+ channels,
+ )
}
/// Returns the Id of the shard associated with the guild.
@@ -1360,20 +1466,6 @@ impl Guild {
/// When the cache is enabled this will automatically retrieve the total
/// number of shards.
///
- /// **Note**: When the cache is enabled, this function unlocks the cache to
- /// retrieve the total number of shards in use. If you already have the
- /// total, consider using [`utils::shard_id`].
- ///
- /// [`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() }
-
- /// Returns the Id of the shard associated with the guild.
- ///
- /// When the cache is enabled this will automatically retrieve the total
- /// number of shards.
- ///
/// When the cache is not enabled, the total number of shards being used
/// will need to be passed.
///
@@ -1389,7 +1481,7 @@ impl Guild {
///
/// assert_eq!(guild.shard_id(17), 7);
/// ```
- #[cfg(all(feature = "utils", not(feature = "cache")))]
+ #[cfg(feature = "utils")]
#[inline]
pub fn shard_id(&self, shard_count: u64) -> u64 { self.id.shard_id(shard_count) }
@@ -1406,8 +1498,12 @@ impl Guild {
///
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
#[inline]
- pub fn start_integration_sync<I: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
- self.id.start_integration_sync(integration_id)
+ pub fn start_integration_sync<I>(&self, integration_id: I)
+ -> FutureResult<()> where I: Into<IntegrationId> {
+ ftryopt!(self.client).http.start_integration_sync(
+ self.id.0,
+ integration_id.into().0,
+ )
}
/// Starts a prune of [`Member`]s.
@@ -1425,17 +1521,19 @@ impl Guild {
/// [`GuildPrune`]: struct.GuildPrune.html
/// [`Member`]: struct.Member.html
/// [Kick Members]: permissions/constant.KICK_MEMBERS.html
- pub fn start_prune(&self, days: u16) -> Result<GuildPrune> {
+ pub fn start_prune(&self, days: u16) -> FutureResult<GuildPrune> {
#[cfg(feature = "cache")]
{
let req = Permissions::KICK_MEMBERS;
if !self.has_perms(req) {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.id.start_prune(days)
+ ftryopt!(self.client).http.start_guild_prune(self.id.0, days)
}
/// Unbans the given [`User`] from the guild.
@@ -1450,17 +1548,19 @@ impl Guild {
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [`User`]: struct.User.html
/// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- pub fn unban<U: Into<UserId>>(&self, user_id: U) -> Result<()> {
+ pub fn unban<U: Into<UserId>>(&self, user_id: U) -> FutureResult<()> {
#[cfg(feature = "cache")]
{
let req = Permissions::BAN_MEMBERS;
if !self.has_perms(req) {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- self.id.unban(user_id)
+ ftryopt!(self.client).http.remove_ban(self.id.0, user_id.into().0)
}
/// Retrieves the guild's webhooks.
@@ -1469,7 +1569,9 @@ impl Guild {
///
/// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
#[inline]
- pub fn webhooks(&self) -> Result<Vec<Webhook>> { self.id.webhooks() }
+ pub fn webhooks(&self) -> FutureResult<Vec<Webhook>> {
+ ftryopt!(self.client).http.get_guild_webhooks(self.id.0)
+ }
/// Obtain a reference to a role by its name.
///
@@ -1502,8 +1604,15 @@ impl Guild {
///
/// client.start().unwrap();
/// ```
- pub fn role_by_name(&self, role_name: &str) -> Option<&Role> {
- self.roles.values().find(|role| role_name == role.name)
+ pub fn role_by_name(&self, role_name: &str) -> Option<&Rc<RefCell<Role>>> {
+ self.roles.values().find(|role| {
+ let role = match role.try_borrow().ok() {
+ Some(role) => role,
+ None => return false,
+ };
+
+ role_name == role.name
+ })
}
}
@@ -1643,6 +1752,7 @@ impl<'de> Deserialize<'de> for Guild {
application_id: application_id,
afk_timeout: afk_timeout,
channels: channels,
+ client: None,
default_message_notifications: default_message_notifications,
emojis: emojis,
explicit_content_filter: explicit_content_filter,
diff --git a/src/model/guild/partial_guild.rs b/src/model/guild/partial_guild.rs
index aebd6e7..6b0b3d2 100644
--- a/src/model/guild/partial_guild.rs
+++ b/src/model/guild/partial_guild.rs
@@ -1,5 +1,10 @@
+use futures::{Future, future};
use model::prelude::*;
+use std::cell::RefCell;
+use std::rc::Rc;
use super::super::utils::{deserialize_emojis, deserialize_roles};
+use super::super::WrappedClient;
+use ::FutureResult;
#[cfg(feature = "model")]
use builder::{EditGuild, EditMember, EditRole};
@@ -28,9 +33,13 @@ pub struct PartialGuild {
pub name: String,
pub owner_id: UserId,
pub region: String,
- #[serde(deserialize_with = "deserialize_roles")] pub roles: HashMap<RoleId, Role>,
+ #[serde(deserialize_with = "deserialize_roles",
+ serialize_with = "serialize_gen_rc_map")]
+ pub roles: HashMap<RoleId, Rc<RefCell<Role>>>,
pub splash: Option<String>,
pub verification_level: VerificationLevel,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
@@ -59,14 +68,21 @@ impl PartialGuild {
/// enum.ModelError.html#variant.DeleteMessageDaysAmount
/// [`User`]: struct.User.html
/// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- pub fn ban<U: Into<UserId>>(&self, user: U, delete_message_days: u8) -> Result<()> {
+ // todo: add ban reason
+ pub fn ban<U: Into<UserId>>(&self, user_id: U, delete_message_days: u8)
+ -> FutureResult<()> {
if delete_message_days > 7 {
- return Err(Error::Model(
+ return Box::new(future::err(Error::Model(
ModelError::DeleteMessageDaysAmount(delete_message_days),
- ));
+ )));
}
- self.id.ban(user, &delete_message_days)
+ Box::new(ftryopt!(self.client).http.ban_user(
+ self.id.0,
+ user_id.into().0,
+ delete_message_days,
+ "",
+ ))
}
/// Gets a list of the guild's bans.
@@ -75,13 +91,30 @@ impl PartialGuild {
///
/// [Ban Members]: permissions/constant.BAN_MEMBERS.html
#[inline]
- pub fn bans(&self) -> Result<Vec<Ban>> { self.id.bans() }
+ pub fn bans(&self) -> FutureResult<Vec<Ban>> {
+ ftryopt!(self.client).http.get_bans(self.id.0)
+ }
/// Gets all of the guild's channels over the REST API.
///
/// [`Guild`]: struct.Guild.html
#[inline]
- pub fn channels(&self) -> Result<HashMap<ChannelId, GuildChannel>> { self.id.channels() }
+ pub fn channels(&self) -> FutureResult<HashMap<ChannelId, GuildChannel>> {
+ let done = ftryopt!(self.client)
+ .http
+ .get_channels(self.id.0)
+ .map(|channels| {
+ let mut map = HashMap::with_capacity(channels.len());
+
+ for channel in channels {
+ map.insert(channel.id, channel);
+ }
+
+ map
+ });
+
+ Box::new(done)
+ }
/// Creates a [`GuildChannel`] in the guild.
///
@@ -103,9 +136,14 @@ impl PartialGuild {
/// [`http::create_channel`]: ../http/fn.create_channel.html
/// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
#[inline]
- pub fn create_channel<C>(&self, name: &str, kind: ChannelType, category: C) -> Result<GuildChannel>
- where C: Into<Option<ChannelId>> {
- self.id.create_channel(name, kind, category)
+ pub fn create_channel<C>(&self, name: &str, kind: ChannelType, category: C)
+ -> FutureResult<GuildChannel> where C: Into<Option<ChannelId>> {
+ ftryopt!(self.client).http.create_channel(
+ self.id.0,
+ name,
+ kind,
+ category.into().map(|x| x.0),
+ )
}
/// Creates an emoji in the guild with a name and base64-encoded image.
@@ -126,8 +164,8 @@ impl PartialGuild {
/// [`utils::read_image`]: ../utils/fn.read_image.html
/// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
#[inline]
- pub fn create_emoji(&self, name: &str, image: &str) -> Result<Emoji> {
- self.id.create_emoji(name, image)
+ pub fn create_emoji(&self, name: &str, image: &str) -> FutureResult<Emoji> {
+ ftryopt!(self.client).http.create_emoji(self.id.0, name, image)
}
/// Creates an integration for the guild.
@@ -136,9 +174,13 @@ impl PartialGuild {
///
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
#[inline]
- pub fn create_integration<I>(&self, integration_id: I, kind: &str) -> Result<()>
- where I: Into<IntegrationId> {
- self.id.create_integration(integration_id, kind)
+ pub fn create_integration<I>(&self, integration_id: I, kind: &str)
+ -> FutureResult<()> where I: Into<IntegrationId> {
+ ftryopt!(self.client).http.create_guild_integration(
+ self.id.0,
+ integration_id.into().0,
+ kind,
+ )
}
/// Creates a new role in the guild with the data set, if any.
@@ -156,8 +198,9 @@ impl PartialGuild {
/// [`Guild::create_role`]: struct.Guild.html#method.create_role
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
#[inline]
- pub fn create_role<F: FnOnce(EditRole) -> EditRole>(&self, f: F) -> Result<Role> {
- self.id.create_role(f)
+ pub fn create_role<F: FnOnce(EditRole) -> EditRole>(&self, f: F)
+ -> FutureResult<Role> {
+ ftryopt!(self.client).http.create_role(self.id.0, f)
}
/// Deletes the current guild if the current user is the owner of the
@@ -165,7 +208,9 @@ impl PartialGuild {
///
/// **Note**: Requires the current user to be the owner of the guild.
#[inline]
- pub fn delete(&self) -> Result<PartialGuild> { self.id.delete() }
+ pub fn delete(&self) -> FutureResult<PartialGuild> {
+ ftryopt!(self.client).http.delete_guild(self.id.0)
+ }
/// Deletes an [`Emoji`] from the guild.
///
@@ -174,8 +219,9 @@ impl PartialGuild {
/// [`Emoji`]: struct.Emoji.html
/// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
#[inline]
- pub fn delete_emoji<E: Into<EmojiId>>(&self, emoji_id: E) -> Result<()> {
- self.id.delete_emoji(emoji_id)
+ pub fn delete_emoji<E: Into<EmojiId>>(&self, emoji_id: E)
+ -> FutureResult<()> {
+ ftryopt!(self.client).http.delete_emoji(self.id.0, emoji_id.into().0)
}
/// Deletes an integration by Id from the guild.
@@ -184,8 +230,12 @@ impl PartialGuild {
///
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
#[inline]
- pub fn delete_integration<I: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
- self.id.delete_integration(integration_id)
+ pub fn delete_integration<I: Into<IntegrationId>>(&self, integration_id: I)
+ -> FutureResult<()> {
+ ftryopt!(self.client).http.delete_guild_integration(
+ self.id.0,
+ integration_id.into().0,
+ )
}
/// Deletes a [`Role`] by Id from the guild.
@@ -199,8 +249,10 @@ impl PartialGuild {
/// [`Role::delete`]: struct.Role.html#method.delete
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
#[inline]
- pub fn delete_role<R: Into<RoleId>>(&self, role_id: R) -> Result<()> {
- self.id.delete_role(role_id)
+ pub fn delete_role<R: Into<RoleId>>(&self, role_id: R) -> FutureResult<()> {
+ let role_id = role_id.into().0;
+
+ ftryopt!(self.client).http.delete_role(self.id.0, role_id)
}
/// Edits the current guild with new data where specified.
@@ -209,28 +261,9 @@ impl PartialGuild {
/// permission.
///
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- pub fn edit<F>(&mut self, f: F) -> Result<()>
- where F: FnOnce(EditGuild) -> EditGuild {
- match self.id.edit(f) {
- Ok(guild) => {
- self.afk_channel_id = guild.afk_channel_id;
- self.afk_timeout = guild.afk_timeout;
- self.default_message_notifications = guild.default_message_notifications;
- self.emojis = guild.emojis;
- self.features = guild.features;
- self.icon = guild.icon;
- self.mfa_level = guild.mfa_level;
- self.name = guild.name;
- self.owner_id = guild.owner_id;
- self.region = guild.region;
- self.roles = guild.roles;
- self.splash = guild.splash;
- self.verification_level = guild.verification_level;
-
- Ok(())
- },
- Err(why) => Err(why),
- }
+ pub fn edit<'a, F: FnOnce(EditGuild) -> EditGuild>(&self, f: F)
+ -> FutureResult<PartialGuild> {
+ ftryopt!(self.client).http.edit_guild(self.id.0, f)
}
/// Edits an [`Emoji`]'s name in the guild.
@@ -244,8 +277,11 @@ impl PartialGuild {
/// [`Emoji::edit`]: struct.Emoji.html#method.edit
/// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
#[inline]
- pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str) -> Result<Emoji> {
- self.id.edit_emoji(emoji_id, name)
+ pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str)
+ -> FutureResult<Emoji> {
+ ftryopt!(self.client)
+ .http
+ .edit_emoji(self.id.0, emoji_id.into().0, name)
}
/// Edits the properties of member of the guild, such as muting or
@@ -264,9 +300,9 @@ impl PartialGuild {
/// GuildId(7).edit_member(user_id, |m| m.mute(true).roles(&vec![role_id]));
/// ```
#[inline]
- pub fn edit_member<F, U>(&self, user_id: U, f: F) -> Result<()>
+ pub fn edit_member<F, U>(&self, user_id: U, f: F) -> FutureResult<()>
where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> {
- self.id.edit_member(user_id, f)
+ ftryopt!(self.client).http.edit_member(self.id.0, user_id.into().0, f)
}
/// Edits the current user's nickname for the guild.
@@ -284,16 +320,11 @@ impl PartialGuild {
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html
#[inline]
- pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
- self.id.edit_nickname(new_nickname)
+ pub fn edit_nickname(&self, new_nickname: Option<&str>)
+ -> FutureResult<()> {
+ ftryopt!(self.client).http.edit_nickname(self.id.0, new_nickname)
}
- /// Gets a partial amount of guild data by its Id.
- ///
- /// Requires that the current user be in the guild.
- #[inline]
- pub fn get<G: Into<GuildId>>(guild_id: G) -> Result<PartialGuild> { guild_id.into().get() }
-
/// Kicks a [`Member`] from the guild.
///
/// Requires the [Kick Members] permission.
@@ -301,7 +332,9 @@ impl PartialGuild {
/// [`Member`]: struct.Member.html
/// [Kick Members]: permissions/constant.KICK_MEMBERS.html
#[inline]
- pub fn kick<U: Into<UserId>>(&self, user_id: U) -> Result<()> { self.id.kick(user_id) }
+ pub fn kick<U: Into<UserId>>(&self, user_id: U) -> FutureResult<()> {
+ ftryopt!(self.client).http.kick_member(self.id.0, user_id.into().0)
+ }
/// Returns a formatted URL of the guild's icon, if the guild has an icon.
pub fn icon_url(&self) -> Option<String> {
@@ -314,7 +347,9 @@ impl PartialGuild {
///
/// This performs a request over the REST API.
#[inline]
- pub fn integrations(&self) -> Result<Vec<Integration>> { self.id.integrations() }
+ pub fn integrations(&self) -> FutureResult<Vec<Integration>> {
+ ftryopt!(self.client).http.get_guild_integrations(self.id.0)
+ }
/// Gets all of the guild's invites.
///
@@ -322,17 +357,23 @@ impl PartialGuild {
///
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
#[inline]
- pub fn invites(&self) -> Result<Vec<RichInvite>> { self.id.invites() }
+ pub fn invites(&self) -> FutureResult<Vec<RichInvite>> {
+ ftryopt!(self.client).http.get_guild_invites(self.id.0)
+ }
/// Leaves the guild.
#[inline]
- pub fn leave(&self) -> Result<()> { self.id.leave() }
+ pub fn leave(&self) -> FutureResult<()> {
+ ftryopt!(self.client).http.leave_guild(self.id.0)
+ }
/// Gets a user's [`Member`] for the guild by Id.
///
/// [`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, user_id: U) -> FutureResult<Member> {
+ ftryopt!(self.client).http.get_member(self.id.0, user_id.into().0)
+ }
/// Gets a list of the guild's members.
///
@@ -341,9 +382,11 @@ impl PartialGuild {
/// [`User`]'s Id.
///
/// [`User`]: struct.User.html
- pub fn members<U>(&self, limit: Option<u64>, after: Option<U>) -> Result<Vec<Member>>
- where U: Into<UserId> {
- self.id.members(limit, after)
+ pub fn members<U: Into<UserId>>(&self, limit: Option<u64>, after: Option<U>)
+ -> FutureResult<Vec<Member>> {
+ let after = after.map(Into::into).map(|x| x.0);
+
+ ftryopt!(self.client).http.get_guild_members(self.id.0, limit, after)
}
/// Moves a member to a specific voice channel.
@@ -352,9 +395,12 @@ impl PartialGuild {
///
/// [Move Members]: permissions/constant.MOVE_MEMBERS.html
#[inline]
- pub fn move_member<C, U>(&self, user_id: U, channel_id: C) -> Result<()>
- where C: Into<ChannelId>, U: Into<UserId> {
- self.id.move_member(user_id, channel_id)
+ pub fn move_member<C, U>(&self, user_id: U, channel_id: C)
+ -> FutureResult<()> where C: Into<ChannelId>, U: Into<UserId> {
+ ftryopt!(self.client)
+ .http
+ .edit_member(self.id.0, user_id.into().0, |f| f
+ .voice_channel(channel_id.into().0))
}
/// Gets the number of [`Member`]s that would be pruned with the given
@@ -365,21 +411,9 @@ impl PartialGuild {
/// [`Member`]: struct.Member.html
/// [Kick Members]: permissions/constant.KICK_MEMBERS.html
#[inline]
- pub fn prune_count(&self, days: u16) -> Result<GuildPrune> { self.id.prune_count(days) }
-
- /// Returns the Id of the shard associated with the guild.
- ///
- /// When the cache is enabled this will automatically retrieve the total
- /// number of shards.
- ///
- /// **Note**: When the cache is enabled, this function unlocks the cache to
- /// retrieve the total number of shards in use. If you already have the
- /// total, consider using [`utils::shard_id`].
- ///
- /// [`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 prune_count(&self, days: u16) -> FutureResult<GuildPrune> {
+ ftryopt!(self.client).http.get_guild_prune_count(self.id.0, days)
+ }
/// Returns the Id of the shard associated with the guild.
///
@@ -401,7 +435,7 @@ impl PartialGuild {
///
/// assert_eq!(guild.shard_id(17), 7);
/// ```
- #[cfg(all(feature = "utils", not(feature = "cache")))]
+ #[cfg(feature = "utils")]
#[inline]
pub fn shard_id(&self, shard_count: u64) -> u64 { self.id.shard_id(shard_count) }
@@ -418,8 +452,11 @@ impl PartialGuild {
///
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
#[inline]
- pub fn start_integration_sync<I: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
- self.id.start_integration_sync(integration_id)
+ pub fn start_integration_sync<I>(&self, integration_id: I)
+ -> FutureResult<()> where I: Into<IntegrationId> {
+ ftryopt!(self.client)
+ .http
+ .start_integration_sync(self.id.0, integration_id.into().0)
}
/// Unbans a [`User`] from the guild.
@@ -429,7 +466,9 @@ impl PartialGuild {
/// [`User`]: struct.User.html
/// [Ban Members]: permissions/constant.BAN_MEMBERS.html
#[inline]
- pub fn unban<U: Into<UserId>>(&self, user_id: U) -> Result<()> { self.id.unban(user_id) }
+ pub fn unban<U: Into<UserId>>(&self, user_id: U) -> FutureResult<()> {
+ ftryopt!(self.client).http.remove_ban(self.id.0, user_id.into().0)
+ }
/// Retrieves the guild's webhooks.
///
@@ -437,7 +476,9 @@ impl PartialGuild {
///
/// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
#[inline]
- pub fn webhooks(&self) -> Result<Vec<Webhook>> { self.id.webhooks() }
+ pub fn webhooks(&self) -> FutureResult<Vec<Webhook>> {
+ ftryopt!(self.client).http.get_guild_webhooks(self.id.0)
+ }
/// Obtain a reference to a role by its name.
///
@@ -471,7 +512,7 @@ impl PartialGuild {
///
/// client.start().unwrap();
/// ```
- pub fn role_by_name(&self, role_name: &str) -> Option<&Role> {
- self.roles.values().find(|role| role_name == role.name)
+ pub fn role_by_name(&self, role_name: &str) -> Option<&Rc<RefCell<Role>>> {
+ self.roles.values().find(|role| role_name == role.borrow().name)
}
}
diff --git a/src/model/guild/role.rs b/src/model/guild/role.rs
index 53ec478..bcb142a 100644
--- a/src/model/guild/role.rs
+++ b/src/model/guild/role.rs
@@ -1,12 +1,12 @@
use model::prelude::*;
use std::cmp::Ordering;
+use super::super::WrappedClient;
+use ::FutureResult;
#[cfg(all(feature = "builder", feature = "cache", feature = "model"))]
use builder::EditRole;
#[cfg(all(feature = "cache", feature = "model"))]
use internal::prelude::*;
-#[cfg(all(feature = "cache", feature = "model"))]
-use {CACHE, http};
/// Information about a role within a guild. A role represents a set of
/// permissions, and can be attached to one or multiple users. A role has
@@ -57,6 +57,8 @@ pub struct Role {
///
/// The `@everyone` role is usually either `-1` or `0`.
pub position: i64,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
@@ -68,7 +70,11 @@ impl Role {
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
#[cfg(feature = "cache")]
#[inline]
- pub fn delete(&self) -> Result<()> { http::delete_role(self.find_guild()?.0, self.id.0) }
+ pub fn delete(&self) -> FutureResult<()> {
+ let guild_id = ftry!(self.find_guild());
+
+ ftryopt!(self.client).http.delete_role(guild_id.0, self.id.0)
+ }
/// Edits a [`Role`], optionally setting its new fields.
///
@@ -89,9 +95,11 @@ impl Role {
/// [`Role`]: struct.Role.html
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
#[cfg(all(feature = "builder", feature = "cache"))]
- pub fn edit<F: FnOnce(EditRole) -> EditRole>(&self, f: F) -> Result<Role> {
- self.find_guild()
- .and_then(|guild_id| guild_id.edit_role(self.id, f))
+ pub fn edit<F: FnOnce(EditRole) -> EditRole>(&self, f: F)
+ -> FutureResult<Role> {
+ let guild_id = ftry!(self.find_guild());
+
+ ftryopt!(self.client).http.edit_role(guild_id.0, self.id.0, f)
}
/// Searches the cache for the guild that owns the role.
@@ -104,8 +112,12 @@ impl Role {
/// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
#[cfg(feature = "cache")]
pub fn find_guild(&self) -> Result<GuildId> {
- for guild in CACHE.read().guilds.values() {
- let guild = guild.read();
+ let client = self.client.as_ref().ok_or_else(|| {
+ Error::Model(ModelError::ClientNotPresent)
+ })?;
+
+ for guild in client.cache.borrow().guilds.values() {
+ let guild = guild.borrow();
if guild.roles.contains_key(&RoleId(self.id.0)) {
return Ok(guild.id);
@@ -161,29 +173,6 @@ impl PartialOrd for Role {
fn partial_cmp(&self, other: &Role) -> Option<Ordering> { Some(self.cmp(other)) }
}
-#[cfg(feature = "model")]
-impl RoleId {
- /// Search the cache for the role.
- #[cfg(feature = "cache")]
- pub fn find(&self) -> Option<Role> {
- let cache = CACHE.read();
-
- for guild in cache.guilds.values() {
- let guild = guild.read();
-
- if !guild.roles.contains_key(self) {
- continue;
- }
-
- if let Some(role) = guild.roles.get(self) {
- return Some(role.clone());
- }
- }
-
- None
- }
-}
-
impl From<Role> for RoleId {
/// Gets the Id of a role.
fn from(role: Role) -> RoleId { role.id }
diff --git a/src/model/invite.rs b/src/model/invite.rs
index 6573e35..3be7f47 100644
--- a/src/model/invite.rs
+++ b/src/model/invite.rs
@@ -1,16 +1,13 @@
//! Models for server and channel invites.
use chrono::{DateTime, FixedOffset};
+use futures::future;
use super::prelude::*;
+use super::WrappedClient;
+use ::FutureResult;
-#[cfg(feature = "model")]
-use builder::CreateInvite;
-#[cfg(feature = "model")]
-use internal::prelude::*;
#[cfg(all(feature = "cache", feature = "model"))]
-use super::{Permissions, utils as model_utils};
-#[cfg(feature = "model")]
-use {http, utils};
+use super::Permissions;
/// Information about an invite code.
///
@@ -40,46 +37,12 @@ pub struct Invite {
/// a representation of the minimal amount of information needed about the
/// [`Guild`] being invited to.
pub guild: InviteGuild,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
impl Invite {
- /// Creates an invite for a [`GuildChannel`], providing a builder so that
- /// fields may optionally be set.
- ///
- /// See the documentation for the [`CreateInvite`] builder for information
- /// on how to use this and the default values that it provides.
- ///
- /// Requires the [Create Invite] permission.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
- /// if the current user does not have the required [permission].
- ///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [`CreateInvite`]: ../builder/struct.CreateInvite.html
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [Create Invite]: permissions/constant.CREATE_INVITE.html
- /// [permission]: permissions/index.html
- pub fn create<C, F>(channel_id: C, f: F) -> Result<RichInvite>
- where C: Into<ChannelId>, F: FnOnce(CreateInvite) -> CreateInvite {
- let channel_id = channel_id.into();
-
- #[cfg(feature = "cache")]
- {
- let req = Permissions::CREATE_INVITE;
-
- if !model_utils::user_has_perms(channel_id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
- }
- }
-
- let map = utils::vecmap_to_json_map(f(CreateInvite::default()).0);
-
- http::create_invite(channel_id.0, &map)
- }
-
/// Deletes the invite.
///
/// **Note**: Requires the [Manage Guild] permission.
@@ -92,30 +55,23 @@ impl Invite {
/// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
/// [permission]: permissions/index.html
- pub fn delete(&self) -> Result<Invite> {
+ pub fn delete(&self) -> FutureResult<Invite> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
+ let cache = ftry!(client.cache.try_borrow());
+
let req = Permissions::MANAGE_GUILD;
- if !model_utils::user_has_perms(self.channel.id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(cache.user_has_perms(self.channel.id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- http::delete_invite(&self.code)
- }
-
- /// Gets the information about an invite.
- #[allow(unused_mut)]
- pub fn get(code: &str, stats: bool) -> Result<Invite> {
- let mut invite = code;
-
- #[cfg(feature = "utils")]
- {
- invite = ::utils::parse_invite(invite);
- }
-
- http::get_invite(invite, stats)
+ ftryopt!(self.client).http.delete_invite(&self.code)
}
/// Returns a URL to use for the invite.
@@ -177,20 +133,6 @@ impl InviteGuild {
/// When the cache is enabled this will automatically retrieve the total
/// number of shards.
///
- /// **Note**: When the cache is enabled, this function unlocks the cache to
- /// retrieve the total number of shards in use. If you already have the
- /// total, consider using [`utils::shard_id`].
- ///
- /// [`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() }
-
- /// Returns the Id of the shard associated with the guild.
- ///
- /// When the cache is enabled this will automatically retrieve the total
- /// number of shards.
- ///
/// When the cache is not enabled, the total number of shards being used
/// will need to be passed.
///
@@ -206,9 +148,11 @@ impl InviteGuild {
///
/// assert_eq!(guild.shard_id(17), 7);
/// ```
- #[cfg(all(feature = "utils", not(feature = "cache")))]
+ #[cfg(feature = "utils")]
#[inline]
- pub fn shard_id(&self, shard_count: u64) -> u64 { self.id.shard_id(shard_count) }
+ pub fn shard_id(&self, shard_count: u64) -> u64 {
+ self.id.shard_id(shard_count)
+ }
}
/// Detailed information about an invite.
@@ -250,6 +194,8 @@ pub struct RichInvite {
pub temporary: bool,
/// The amount of times that an invite has been used.
pub uses: u64,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
@@ -271,17 +217,23 @@ impl RichInvite {
/// [`http::delete_invite`]: ../http/fn.delete_invite.html
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
/// [permission]: permissions/index.html
- pub fn delete(&self) -> Result<Invite> {
+ pub fn delete(&self) -> FutureResult<Invite> {
+ let client = ftryopt!(self.client);
+
#[cfg(feature = "cache")]
{
+ let cache = ftry!(client.cache.try_borrow());
+
let req = Permissions::MANAGE_GUILD;
- if !model_utils::user_has_perms(self.channel.id, req)? {
- return Err(Error::Model(ModelError::InvalidPermissions(req)));
+ if !ftry!(cache.user_has_perms(self.channel.id, req)) {
+ return Box::new(future::err(Error::Model(
+ ModelError::InvalidPermissions(req),
+ )));
}
}
- http::delete_invite(&self.code)
+ ftryopt!(self.client).http.delete_invite(&self.code)
}
/// Returns a URL to use for the invite.
diff --git a/src/model/misc.rs b/src/model/misc.rs
index 183ddc3..898ce6f 100644
--- a/src/model/misc.rs
+++ b/src/model/misc.rs
@@ -1,7 +1,6 @@
//! Miscellaneous helper traits, enums, and structs for models.
use super::prelude::*;
-use internal::RwLockExt;
#[cfg(all(feature = "model", feature = "utils"))]
use std::error::Error as StdError;
@@ -28,9 +27,9 @@ impl Mentionable for ChannelId {
impl Mentionable for Channel {
fn mention(&self) -> String {
match *self {
- Channel::Guild(ref x) => format!("<#{}>", x.with(|x| x.id.0)),
- Channel::Private(ref x) => format!("<#{}>", x.with(|x| x.id.0)),
- Channel::Group(ref x) => format!("<#{}>", x.with(|x| x.channel_id.0)),
+ Channel::Guild(ref x) => format!("<#{}>", x.borrow().id.0),
+ Channel::Private(ref x) => format!("<#{}>", x.borrow().id.0),
+ Channel::Group(ref x) => format!("<#{}>", x.borrow().channel_id.0),
Channel::Category(_) => panic!("Categories can't be mentioned"),
}
}
@@ -40,10 +39,6 @@ impl Mentionable for Emoji {
fn mention(&self) -> String { format!("<:{}:{}>", self.name, self.id.0) }
}
-impl Mentionable for Member {
- fn mention(&self) -> String { format!("<@{}>", self.user.with(|u| u.id.0)) }
-}
-
impl Mentionable for RoleId {
fn mention(&self) -> String { format!("<@&{}>", self.0) }
}
@@ -84,20 +79,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)
- .get()
- .map_err(|e| UserParseError::Rest(Box::new(e))),
- _ => Err(UserParseError::InvalidUsername),
- }
- }
-}
-
macro_rules! impl_from_str {
(id: $($id:tt, $err:ident;)*) => {
$(
@@ -136,49 +117,6 @@ macro_rules! impl_from_str {
}
)*
};
-
- (struct: $($struct:ty, $id:tt, $err:ident, $invalid_variant:tt, $parse_fn:ident, $desc:expr;)*) => {
- $(
- #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
- #[derive(Debug)]
- pub enum $err {
- NotPresentInCache,
- $invalid_variant,
- }
-
- #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
- impl fmt::Display for $err {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
- }
-
- #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
- impl StdError for $err {
- fn description(&self) -> &str {
- use self::$err::*;
-
- match *self {
- NotPresentInCache => "not present in cache",
- $invalid_variant => $desc,
- }
- }
- }
-
- #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
- impl FromStr for $struct {
- type Err = $err;
-
- fn from_str(s: &str) -> StdResult<Self, Self::Err> {
- match utils::$parse_fn(s) {
- Some(x) => match $id(x).find() {
- Some(user) => Ok(user),
- _ => Err($err::NotPresentInCache),
- },
- _ => Err($err::$invalid_variant),
- }
- }
- }
- )*
- };
}
impl_from_str! { id:
@@ -187,11 +125,6 @@ impl_from_str! { id:
ChannelId, ChannelIdParseError;
}
-impl_from_str! { struct:
- Channel, ChannelId, ChannelParseError, InvalidChannel, parse_channel, "invalid channel";
- Role, RoleId, RoleParseError, InvalidRole, parse_role, "invalid role";
-}
-
/// A version of an emoji used only when solely the Id and name are known.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct EmojiIdentifier {
diff --git a/src/model/mod.rs b/src/model/mod.rs
index 5e35e92..44a4a3e 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -40,16 +40,17 @@ pub mod webhook;
pub use self::error::Error as ModelError;
pub use self::permissions::Permissions;
+use client::Client;
use internal::prelude::*;
-use parking_lot::RwLock;
use self::utils::*;
use serde::de::Visitor;
+use serde::{Deserialize, Deserializer};
use std::collections::HashMap;
use std::fmt::{Display, Formatter, Result as FmtResult};
-use std::sync::Arc;
+use std::rc::Rc;
+use std::result::Result as StdResult;
#[cfg(feature = "utils")]
use utils::Colour;
-use serde::{Deserialize, Deserializer};
-use std::result::Result as StdResult;
+type WrappedClient = Option<Rc<Client>>;
diff --git a/src/model/user.rs b/src/model/user.rs
index 227fadf..efc880a 100644
--- a/src/model/user.rs
+++ b/src/model/user.rs
@@ -1,29 +1,24 @@
//! User information-related models.
+use futures::{Future, future};
use serde_json;
use std::fmt;
use super::utils::deserialize_u16;
use super::prelude::*;
+use super::WrappedClient;
+use ::FutureResult;
use internal::prelude::*;
use model::misc::Mentionable;
-#[cfg(all(feature = "cache", feature = "model"))]
-use CACHE;
#[cfg(feature = "model")]
use builder::{CreateMessage, EditProfile};
#[cfg(feature = "model")]
use chrono::NaiveDateTime;
#[cfg(feature = "model")]
-use http::{self, GuildPagination};
-#[cfg(all(feature = "cache", feature = "model"))]
-use parking_lot::RwLock;
+use http::GuildPagination;
#[cfg(feature = "model")]
use std::fmt::Write;
#[cfg(feature = "model")]
-use std::mem;
-#[cfg(all(feature = "cache", feature = "model"))]
-use std::sync::Arc;
-#[cfg(feature = "model")]
use utils::{self, VecMap};
/// Information about the current user.
@@ -37,6 +32,8 @@ pub struct CurrentUser {
pub mfa_enabled: bool,
#[serde(rename = "username")] pub name: String,
pub verified: bool,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
@@ -88,8 +85,8 @@ impl CurrentUser {
///
/// CACHE.write().user.edit(|p| p.avatar(Some(&avatar)));
/// ```
- pub fn edit<F>(&mut self, f: F) -> Result<()>
- where F: FnOnce(EditProfile) -> EditProfile {
+ pub fn edit<F: FnOnce(EditProfile) -> EditProfile>(&mut self, f: F)
+ -> Box<Future<Item = CurrentUser, Error = Error>> {
let mut map = VecMap::new();
map.insert("username", Value::String(self.name.clone()));
@@ -98,15 +95,9 @@ impl CurrentUser {
}
let map = utils::vecmap_to_json_map(f(EditProfile(map)).0);
+ let value = Value::Object(map);
- match http::edit_profile(&map) {
- Ok(new) => {
- let _ = mem::replace(self, new);
-
- Ok(())
- },
- Err(why) => Err(why),
- }
+ ftryopt!(self.client).http.edit_profile(&value)
}
/// Retrieves the URL to the current user's avatar, falling back to the
@@ -142,8 +133,10 @@ impl CurrentUser {
/// }
/// }
/// ```
- pub fn guilds(&self) -> Result<Vec<GuildInfo>> {
- http::get_guilds(&GuildPagination::After(GuildId(1)), 100)
+ pub fn guilds(&self) -> FutureResult<Vec<GuildInfo>> {
+ ftryopt!(self.client)
+ .http
+ .get_guilds(&GuildPagination::After(GuildId(1)), 100)
}
/// Returns the invite url for the bot with the given permissions.
@@ -211,20 +204,26 @@ impl CurrentUser {
///
/// [`Error::Format`]: ../enum.Error.html#variant.Format
/// [`HttpError::InvalidRequest`]: ../http/enum.HttpError.html#variant.InvalidRequest
- pub fn invite_url(&self, permissions: Permissions) -> Result<String> {
+ pub fn invite_url(&self, permissions: Permissions) -> FutureResult<String> {
let bits = permissions.bits();
- let client_id = http::get_current_application_info().map(|v| v.id)?;
- let mut url = format!(
- "https://discordapp.com/api/oauth2/authorize?client_id={}&scope=bot",
- client_id
- );
+ let done = ftryopt!(self.client)
+ .http
+ .get_current_application_info()
+ .map(move |app| {
+ let mut url = format!(
+ "https://discordapp.com/api/oauth2/authorize?client_id={}&scope=bot",
+ app.id,
+ );
+
+ if bits != 0 {
+ let _ = write!(url, "&permissions={}", bits);
+ }
- if bits != 0 {
- write!(url, "&permissions={}", bits)?;
- }
+ url
+ });
- Ok(url)
+ Box::new(done)
}
/// Returns a static formatted URL of the user's icon, if one exists.
@@ -357,6 +356,8 @@ pub struct User {
/// change if the username+discriminator pair becomes non-unique.
#[serde(rename = "username")]
pub name: String,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
use std::hash::{Hash, Hasher};
@@ -388,7 +389,9 @@ impl User {
///
/// [current user]: struct.CurrentUser.html
#[inline]
- pub fn create_dm_channel(&self) -> Result<PrivateChannel> { self.id.create_dm_channel() }
+ pub fn create_dm_channel(&self) -> FutureResult<PrivateChannel> {
+ ftryopt!(self.client).http.create_private_channel(self.id.0)
+ }
/// Retrieves the time that this user was created at.
#[inline]
@@ -477,45 +480,56 @@ impl User {
// (AKA: Clippy is wrong and so we have to mark as allowing this lint.)
#[allow(let_and_return)]
#[cfg(feature = "builder")]
- pub fn direct_message<F>(&self, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage {
+ pub fn direct_message<'a, F: 'a + FnOnce(CreateMessage) -> CreateMessage>(
+ &'a self,
+ f: F,
+ ) -> Box<Future<Item = Message, Error = Error> + 'a> {
if self.bot {
- return Err(Error::Model(ModelError::MessagingBot));
+ return Box::new(future::err(Error::Model(
+ ModelError::MessagingBot,
+ )));
}
- let private_channel_id = feature_cache! {
- {
- let finding = {
- let cache = CACHE.read();
+ let client = ftryopt!(self.client);
- let finding = cache.private_channels
- .values()
- .map(|ch| ch.read())
- .find(|ch| ch.recipient.read().id == self.id)
- .map(|ch| ch.id);
+ let private_channel_id = feature_cache! {{
+ let finding = {
+ let cache = client.cache.borrow();
- finding
- };
+ let finding = cache.private_channels
+ .values()
+ .find(|ch| {
+ let ch = ch.borrow();
+ let recipient = ch.recipient.borrow();
- if let Some(finding) = finding {
- finding
- } else {
- let map = json!({
- "recipient_id": self.id.0,
- });
+ recipient.id == self.id
+ })
+ .map(|ch| ch.borrow().id);
- http::create_private_channel(&map)?.id
- }
+ finding
+ };
+
+ if let Some(finding) = finding {
+ return Box::new(client.http.send_message(finding.0, f));
} else {
- let map = json!({
- "recipient_id": self.id.0,
- });
+ let done = client
+ .http
+ .create_private_channel(self.id.0)
+ .map(|channel| channel.id);
- http::create_private_channel(&map)?.id
+ Box::new(done)
}
- };
+ } else {
+ let done = ftryopt!(self.client)
+ .http
+ .create_private_channel(self.id.0)
+ .map(|channel| channel.id);
+
+ Box::new(done)
+ }};
- private_channel_id.send_message(f)
+ Box::new(private_channel_id
+ .and_then(move |id| client.http.send_message(id.0, f)))
}
/// This is an alias of [direct_message].
@@ -539,7 +553,10 @@ impl User {
/// [direct_message]: #method.direct_message
#[cfg(feature = "builder")]
#[inline]
- pub fn dm<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) -> Result<Message> {
+ pub fn dm<'a, F: 'a + FnOnce(CreateMessage) -> CreateMessage>(
+ &'a self,
+ f: F,
+ ) -> Box<Future<Item = Message, Error = Error> + 'a> {
self.direct_message(f)
}
@@ -586,12 +603,19 @@ impl User {
GuildContainer::Guild(guild) => guild.roles.contains_key(&role_id),
GuildContainer::Id(_guild_id) => {
feature_cache! {{
- CACHE.read()
+ let client = match self.client.as_ref() {
+ Some(client) => client,
+ None => return false,
+ };
+
+ let cache = client.cache.borrow();
+
+ cache
.guilds
.get(&_guild_id)
.map(|g| {
- g.read().members.get(&self.id)
- .map(|m| m.roles.contains(&role_id))
+ g.borrow().members.get(&self.id)
+ .map(|m| m.borrow().roles.contains(&role_id))
.unwrap_or(false)
})
.unwrap_or(false)
@@ -602,69 +626,6 @@ impl User {
}
}
- /// Refreshes the information about the user.
- ///
- /// Replaces the instance with the data retrieved over the REST API.
- ///
- /// # Examples
- ///
- /// If maintaing a very long-running bot, you may want to periodically
- /// refresh information about certain users if the state becomes
- /// out-of-sync:
- ///
- /// ```rust,no_run
- /// # use serenity::prelude::*;
- /// # use serenity::model::prelude::*;
- /// #
- /// struct Handler;
- ///
- /// impl EventHandler for Handler {
- /// fn message(&self, _: Context, _: Message) {
- /// // normal message handling here
- /// }
- /// }
- ///
- /// let mut client = Client::new("token", Handler).unwrap();
- /// #
- /// use serenity::model::id::UserId;
- /// use serenity::CACHE;
- /// 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
- /// let handle = thread::spawn(move || {
- /// // 12 hours in seconds
- /// let duration = Duration::from_secs(43200);
- ///
- /// loop {
- /// thread::sleep(duration);
- ///
- /// let cache = CACHE.read();
- ///
- /// for id in &special_users {
- /// if let Some(user) = cache.user(*id) {
- /// if let Err(why) = user.write().refresh() {
- /// println!("Error refreshing {}: {:?}", id, why);
- /// }
- /// }
- /// }
- /// }
- /// });
- ///
- /// println!("{:?}", client.start());
- /// ```
- pub fn refresh(&mut self) -> Result<()> {
- self.id.get().map(|replacement| {
- mem::replace(self, replacement);
-
- ()
- })
- }
-
-
/// Returns a static formatted URL of the user's icon, if one exists.
///
/// This will always produce a WEBP image URL.
@@ -718,40 +679,6 @@ impl fmt::Display for User {
}
}
-#[cfg(feature = "model")]
-impl UserId {
- /// Creates a direct message channel between the [current user] and the
- /// user. This can also retrieve the channel if one already exists.
- ///
- /// [current user]: struct.CurrentUser.html
- pub fn create_dm_channel(&self) -> Result<PrivateChannel> {
- let map = json!({
- "recipient_id": self.0,
- });
-
- http::create_private_channel(&map)
- }
-
- /// Search the cache for the user with the Id.
- #[cfg(feature = "cache")]
- pub fn find(&self) -> Option<Arc<RwLock<User>>> { CACHE.read().user(*self) }
-
- /// Gets a user by its Id over the REST API.
- ///
- /// **Note**: The current user must be a bot user.
- #[inline]
- pub fn get(&self) -> Result<User> {
- #[cfg(feature = "cache")]
- {
- if let Some(user) = CACHE.read().user(*self) {
- return Ok(user.read().clone());
- }
- }
-
- http::get_user(self.0)
- }
-}
-
impl From<CurrentUser> for UserId {
/// Gets the Id of a `CurrentUser` struct.
fn from(current_user: CurrentUser) -> UserId { current_user.id }
@@ -762,16 +689,6 @@ impl<'a> From<&'a CurrentUser> for UserId {
fn from(current_user: &CurrentUser) -> UserId { current_user.id }
}
-impl From<Member> for UserId {
- /// Gets the Id of a `Member`.
- fn from(member: Member) -> UserId { member.user.read().id }
-}
-
-impl<'a> From<&'a Member> for UserId {
- /// Gets the Id of a `Member`.
- fn from(member: &Member) -> UserId { member.user.read().id }
-}
-
impl From<User> for UserId {
/// Gets the Id of a `User`.
fn from(user: User) -> UserId { user.id }
diff --git a/src/model/utils.rs b/src/model/utils.rs
index 981b61b..9ed34e1 100644
--- a/src/model/utils.rs
+++ b/src/model/utils.rs
@@ -1,18 +1,19 @@
-use parking_lot::RwLock;
use serde::de::Error as DeError;
use serde::ser::{SerializeSeq, Serialize, Serializer};
+use std::cell::RefCell;
use std::collections::HashMap;
use std::hash::Hash;
-use std::sync::Arc;
+use std::rc::Rc;
use super::prelude::*;
+#[cfg(all(feature = "cache", feature = "model"))]
+use cache::Cache;
+
#[cfg(feature = "cache")]
use internal::prelude::*;
#[cfg(all(feature = "cache", feature = "model"))]
use super::permissions::Permissions;
-#[cfg(all(feature = "cache", feature = "model"))]
-use CACHE;
pub fn default_true() -> bool {
true
@@ -33,12 +34,12 @@ pub fn deserialize_emojis<'de, D: Deserializer<'de>>(
pub fn deserialize_guild_channels<'de, D: Deserializer<'de>>(
deserializer: D)
- -> StdResult<HashMap<ChannelId, Arc<RwLock<GuildChannel>>>, D::Error> {
+ -> StdResult<HashMap<ChannelId, Rc<RefCell<GuildChannel>>>, D::Error> {
let vec: Vec<GuildChannel> = Deserialize::deserialize(deserializer)?;
let mut map = HashMap::new();
for channel in vec {
- map.insert(channel.id, Arc::new(RwLock::new(channel)));
+ map.insert(channel.id, Rc::new(RefCell::new(channel)));
}
Ok(map)
@@ -46,14 +47,14 @@ pub fn deserialize_guild_channels<'de, D: Deserializer<'de>>(
pub fn deserialize_members<'de, D: Deserializer<'de>>(
deserializer: D)
- -> StdResult<HashMap<UserId, Member>, D::Error> {
+ -> StdResult<HashMap<UserId, Rc<RefCell<Member>>>, D::Error> {
let vec: Vec<Member> = Deserialize::deserialize(deserializer)?;
let mut members = HashMap::new();
for member in vec {
- let user_id = member.user.read().id;
+ let user_id = member.user.borrow().id;
- members.insert(user_id, member);
+ members.insert(user_id, Rc::new(RefCell::new(member)));
}
Ok(members)
@@ -61,12 +62,12 @@ pub fn deserialize_members<'de, D: Deserializer<'de>>(
pub fn deserialize_presences<'de, D: Deserializer<'de>>(
deserializer: D)
- -> StdResult<HashMap<UserId, Presence>, D::Error> {
+ -> StdResult<HashMap<UserId, Rc<RefCell<Presence>>>, D::Error> {
let vec: Vec<Presence> = Deserialize::deserialize(deserializer)?;
let mut presences = HashMap::new();
for presence in vec {
- presences.insert(presence.user_id, presence);
+ presences.insert(presence.user_id, Rc::new(RefCell::new(presence)));
}
Ok(presences)
@@ -74,19 +75,19 @@ pub fn deserialize_presences<'de, D: Deserializer<'de>>(
pub fn deserialize_private_channels<'de, D: Deserializer<'de>>(
deserializer: D)
- -> StdResult<HashMap<ChannelId, Channel>, D::Error> {
+ -> StdResult<HashMap<ChannelId, Rc<RefCell<Channel>>>, D::Error> {
let vec: Vec<Channel> = Deserialize::deserialize(deserializer)?;
let mut private_channels = HashMap::new();
for private_channel in vec {
let id = match private_channel {
- Channel::Group(ref group) => group.read().channel_id,
- Channel::Private(ref channel) => channel.read().id,
+ Channel::Group(ref group) => group.borrow().channel_id,
+ Channel::Private(ref channel) => channel.borrow().id,
Channel::Guild(_) => unreachable!("Guild private channel decode"),
Channel::Category(_) => unreachable!("Channel category private channel decode"),
};
- private_channels.insert(id, private_channel);
+ private_channels.insert(id, Rc::new(RefCell::new(private_channel)));
}
Ok(private_channels)
@@ -94,12 +95,12 @@ pub fn deserialize_private_channels<'de, D: Deserializer<'de>>(
pub fn deserialize_roles<'de, D: Deserializer<'de>>(
deserializer: D)
- -> StdResult<HashMap<RoleId, Role>, D::Error> {
+ -> StdResult<HashMap<RoleId, Rc<RefCell<Role>>>, D::Error> {
let vec: Vec<Role> = Deserialize::deserialize(deserializer)?;
let mut roles = HashMap::new();
for role in vec {
- roles.insert(role.id, role);
+ roles.insert(role.id, Rc::new(RefCell::new(role)));
}
Ok(roles)
@@ -107,7 +108,7 @@ pub fn deserialize_roles<'de, D: Deserializer<'de>>(
pub fn deserialize_single_recipient<'de, D: Deserializer<'de>>(
deserializer: D)
- -> StdResult<Arc<RwLock<User>>, D::Error> {
+ -> StdResult<Rc<RefCell<User>>, D::Error> {
let mut users: Vec<User> = Deserialize::deserialize(deserializer)?;
let user = if users.is_empty() {
return Err(DeError::custom("Expected a single recipient"));
@@ -115,42 +116,42 @@ pub fn deserialize_single_recipient<'de, D: Deserializer<'de>>(
users.remove(0)
};
- Ok(Arc::new(RwLock::new(user)))
+ Ok(Rc::new(RefCell::new(user)))
}
-pub fn deserialize_sync_user<'de, D>(deserializer: D)
- -> StdResult<Arc<RwLock<User>>, D::Error> where D: Deserializer<'de> {
- Ok(Arc::new(RwLock::new(User::deserialize(deserializer)?)))
+pub fn deserialize_user<'de, D>(deserializer: D)
+ -> StdResult<Rc<RefCell<User>>, D::Error> where D: Deserializer<'de> {
+ Ok(Rc::new(RefCell::new(User::deserialize(deserializer)?)))
}
-pub fn serialize_sync_user<S: Serializer>(
- user: &Arc<RwLock<User>>,
+pub fn serialize_user<S: Serializer>(
+ user: &Rc<RefCell<User>>,
serializer: S,
) -> StdResult<S::Ok, S::Error> {
- User::serialize(&*user.read(), serializer)
+ User::serialize(&*user.borrow(), serializer)
}
pub fn deserialize_users<'de, D: Deserializer<'de>>(
deserializer: D)
- -> StdResult<HashMap<UserId, Arc<RwLock<User>>>, D::Error> {
+ -> StdResult<HashMap<UserId, Rc<RefCell<User>>>, D::Error> {
let vec: Vec<User> = Deserialize::deserialize(deserializer)?;
let mut users = HashMap::new();
for user in vec {
- users.insert(user.id, Arc::new(RwLock::new(user)));
+ users.insert(user.id, Rc::new(RefCell::new(user)));
}
Ok(users)
}
pub fn serialize_users<S: Serializer>(
- users: &HashMap<UserId, Arc<RwLock<User>>>,
+ users: &HashMap<UserId, Rc<RefCell<User>>>,
serializer: S
) -> StdResult<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(users.len()))?;
for user in users.values() {
- seq.serialize_element(&*user.read())?;
+ seq.serialize_element(&*user.borrow())?;
}
seq.end()
@@ -190,58 +191,82 @@ pub fn serialize_gen_map<K: Eq + Hash, S: Serializer, V: Serialize>(
seq.end()
}
-pub fn serialize_gen_locked_map<K: Eq + Hash, S: Serializer, V: Serialize>(
- map: &HashMap<K, Arc<RwLock<V>>>,
+pub fn serialize_gen_rc_map<K: Eq + Hash, S: Serializer, V: Serialize>(
+ map: &HashMap<K, Rc<RefCell<V>>>,
serializer: S,
) -> StdResult<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(map.len()))?;
for value in map.values() {
- seq.serialize_element(&*value.read())?;
+ if let Ok(item) = value.try_borrow() {
+ seq.serialize_element(&*item)?;
+ }
}
seq.end()
}
#[cfg(all(feature = "cache", feature = "model"))]
-pub fn user_has_perms(channel_id: ChannelId, mut permissions: Permissions) -> Result<bool> {
- let cache = CACHE.read();
- let current_user = &cache.user;
+pub trait PermissionCheck {
+ fn user_has_perms(&self, channel_id: ChannelId, permissions: Permissions)
+ -> Result<bool>;
+}
- let channel = match cache.channel(channel_id) {
- Some(channel) => channel,
- None => return Err(Error::Model(ModelError::ItemMissing)),
- };
+#[cfg(all(feature = "cache", feature = "model"))]
+impl PermissionCheck for Cache {
+ fn user_has_perms(
+ &self,
+ channel_id: ChannelId,
+ mut permissions: Permissions,
+ ) -> Result<bool> {
+ let current_user = &self.user;
+
+ let channel = match self.channel(channel_id) {
+ Some(channel) => channel,
+ None => return Err(Error::Model(ModelError::ItemMissing)),
+ };
- let guild_id = match channel {
- Channel::Guild(channel) => channel.read().guild_id,
- Channel::Group(_) | Channel::Private(_) | Channel::Category(_) => {
- // Both users in DMs, and all users in groups and maybe all channels in categories will
- // have the same
- // permissions.
- //
- // The only exception to this is when the current user is blocked by
- // the recipient in a DM channel, which results in the current user
- // not being able to send messages.
- //
- // Since serenity can't _reasonably_ check and keep track of these,
- // just assume that all permissions are granted and return `true`.
- return Ok(true);
- },
- };
+ let guild_id = match channel {
+ Channel::Guild(channel) => channel.borrow().guild_id,
+ Channel::Group(_) | Channel::Private(_) | Channel::Category(_) => {
+ // Both users in DMs, and all users in groups and maybe all channels in categories will
+ // have the same
+ // permissions.
+ //
+ // The only exception to this is when the current user is blocked by
+ // the recipient in a DM channel, which results in the current user
+ // not being able to send messages.
+ //
+ // Since serenity can't _reasonably_ check and keep track of these,
+ // just assume that all permissions are granted and return `true`.
+ return Ok(true);
+ },
+ };
- let guild = match cache.guild(guild_id) {
- Some(guild) => guild,
- None => return Err(Error::Model(ModelError::ItemMissing)),
- };
+ let guild = match self.guilds.get(&guild_id) {
+ Some(guild) => guild,
+ None => return Err(Error::Model(ModelError::ItemMissing)),
+ };
- let perms = guild
- .read()
- .permissions_in(channel_id, current_user.id);
+ let perms = guild
+ .borrow()
+ .permissions_in(channel_id, current_user.id);
- permissions.remove(perms);
+ permissions.remove(perms);
- Ok(permissions.is_empty())
+ Ok(permissions.is_empty())
+ }
+}
+
+macro_rules! ftryopt {
+ ($code:expr) => {
+ match $code {
+ Some(ref v) => v,
+ None => return Box::new(::futures::future::err(::Error::Model(
+ ::model::ModelError::ClientNotPresent,
+ ))),
+ }
+ };
}
macro_rules! num_visitors {
diff --git a/src/model/webhook.rs b/src/model/webhook.rs
index 0d6b7c9..f3003e7 100644
--- a/src/model/webhook.rs
+++ b/src/model/webhook.rs
@@ -1,18 +1,17 @@
//! Webhook model and implementations.
+use futures::Future;
use super::id::{ChannelId, GuildId, WebhookId};
use super::user::User;
+use super::WrappedClient;
+use ::FutureResult;
#[cfg(feature = "model")]
use builder::ExecuteWebhook;
#[cfg(feature = "model")]
use internal::prelude::*;
#[cfg(feature = "model")]
-use std::mem;
-#[cfg(feature = "model")]
use super::channel::Message;
-#[cfg(feature = "model")]
-use {http, utils};
/// A representation of a webhook, which is a low-effort way to post messages to
/// channels. They do not necessarily require a bot user or authentication to
@@ -45,6 +44,8 @@ pub struct Webhook {
///
/// **Note**: This is not received when getting a webhook by its token.
pub user: Option<User>,
+ #[serde(skip)]
+ pub(crate) client: WrappedClient,
}
#[cfg(feature = "model")]
@@ -56,7 +57,13 @@ impl Webhook {
///
/// [`http::delete_webhook_with_token`]: ../http/fn.delete_webhook_with_token.html
#[inline]
- pub fn delete(&self) -> Result<()> { http::delete_webhook_with_token(self.id.0, &self.token) }
+ pub fn delete(&self) -> FutureResult<()> {
+ let done = ftryopt!(self.client)
+ .http
+ .delete_webhook_with_token(self.id.0, &self.token);
+
+ Box::new(done)
+ }
///
/// Edits the webhook in-place. All fields are optional.
@@ -104,36 +111,13 @@ impl Webhook {
///
/// [`http::edit_webhook`]: ../http/fn.edit_webhook.html
/// [`http::edit_webhook_with_token`]: ../http/fn.edit_webhook_with_token.html
- pub fn edit(&mut self, name: Option<&str>, avatar: Option<&str>) -> Result<()> {
- if name.is_none() && avatar.is_none() {
- return Ok(());
- }
-
- let mut map = Map::new();
+ pub fn edit(&mut self, name: Option<&str>, avatar: Option<&str>)
+ -> Box<Future<Item = Webhook, Error = Error>> {
+ let done = ftryopt!(self.client)
+ .http
+ .edit_webhook_with_token(self.id.0, &self.token, name, avatar);
- if let Some(avatar) = avatar {
- map.insert(
- "avatar".to_string(),
- if avatar.is_empty() {
- Value::Null
- } else {
- Value::String(avatar.to_string())
- },
- );
- }
-
- if let Some(name) = name {
- map.insert("name".to_string(), Value::String(name.to_string()));
- }
-
- match http::edit_webhook_with_token(self.id.0, &self.token, &map) {
- Ok(replacement) => {
- mem::replace(self, replacement);
-
- Ok(())
- },
- Err(why) => Err(why),
- }
+ Box::new(done)
}
/// Executes a webhook with the fields set via the given builder.
@@ -184,41 +168,15 @@ impl Webhook {
/// .expect("Error executing");
/// ```
#[inline]
- pub fn execute<F: FnOnce(ExecuteWebhook) -> ExecuteWebhook>(&self,
- wait: bool,
- f: F)
- -> Result<Option<Message>> {
- let map = utils::vecmap_to_json_map(f(ExecuteWebhook::default()).0);
-
- http::execute_webhook(self.id.0, &self.token, wait, &map)
- }
+ pub fn execute<'a, F: FnOnce(ExecuteWebhook) -> ExecuteWebhook>(
+ &'a self,
+ wait: bool,
+ f: F,
+ ) -> Box<Future<Item = Option<Message>, Error = Error> + 'a> {
+ let done = ftryopt!(self.client)
+ .http
+ .execute_webhook(self.id.0, &self.token, wait, f);
- /// Retrieves the latest information about the webhook, editing the
- /// webhook in-place.
- ///
- /// As this calls the [`http::get_webhook_with_token`] function,
- /// authentication is not required.
- ///
- /// [`http::get_webhook_with_token`]: ../http/fn.get_webhook_with_token.html
- pub fn refresh(&mut self) -> Result<()> {
- match http::get_webhook_with_token(self.id.0, &self.token) {
- Ok(replacement) => {
- let _ = mem::replace(self, replacement);
-
- Ok(())
- },
- Err(why) => Err(why),
- }
+ Box::new(done)
}
}
-
-#[cfg(feature = "model")]
-impl WebhookId {
- /// Retrieves the webhook by the Id.
- ///
- /// **Note**: Requires the [Manage Webhooks] permission.
- ///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
- #[inline]
- pub fn get(&self) -> Result<Webhook> { http::get_webhook(self.0) }
-}