diff options
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/channel/attachment.rs | 8 | ||||
| -rw-r--r-- | src/model/channel/channel_category.rs | 18 | ||||
| -rw-r--r-- | src/model/channel/channel_id.rs | 36 | ||||
| -rw-r--r-- | src/model/channel/embed.rs | 29 | ||||
| -rw-r--r-- | src/model/channel/guild_channel.rs | 54 | ||||
| -rw-r--r-- | src/model/channel/message.rs | 32 | ||||
| -rw-r--r-- | src/model/channel/mod.rs | 18 | ||||
| -rw-r--r-- | src/model/channel/private_channel.rs | 2 | ||||
| -rw-r--r-- | src/model/channel/reaction.rs | 2 | ||||
| -rw-r--r-- | src/model/error.rs | 8 | ||||
| -rw-r--r-- | src/model/event.rs | 52 | ||||
| -rw-r--r-- | src/model/gateway.rs | 3 | ||||
| -rw-r--r-- | src/model/guild/audit_log.rs | 109 | ||||
| -rw-r--r-- | src/model/guild/emoji.rs | 4 | ||||
| -rw-r--r-- | src/model/guild/feature.rs | 25 | ||||
| -rw-r--r-- | src/model/guild/guild_id.rs | 31 | ||||
| -rw-r--r-- | src/model/guild/member.rs | 65 | ||||
| -rw-r--r-- | src/model/guild/mod.rs | 102 | ||||
| -rw-r--r-- | src/model/guild/partial_guild.rs | 11 | ||||
| -rw-r--r-- | src/model/guild/role.rs | 8 | ||||
| -rw-r--r-- | src/model/invite.rs | 8 | ||||
| -rw-r--r-- | src/model/misc.rs | 11 | ||||
| -rw-r--r-- | src/model/mod.rs | 5 | ||||
| -rw-r--r-- | src/model/user.rs | 92 | ||||
| -rw-r--r-- | src/model/utils.rs | 13 | ||||
| -rw-r--r-- | src/model/webhook.rs | 11 |
26 files changed, 425 insertions, 332 deletions
diff --git a/src/model/channel/attachment.rs b/src/model/channel/attachment.rs index eac513f..f520cd4 100644 --- a/src/model/channel/attachment.rs +++ b/src/model/channel/attachment.rs @@ -54,7 +54,7 @@ impl Attachment { /// /// /// impl EventHandler for Handler { - /// fn on_message(&self, _: Context, message: Message) { + /// fn message(&self, _: Context, message: Message) { /// for attachment in message.attachments { /// let content = match attachment.download() { /// Ok(content) => content, @@ -86,12 +86,14 @@ impl Attachment { /// } /// } /// - /// fn on_ready(&self, _: Context, ready: Ready) { + /// 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); client.start().unwrap(); + /// let mut client = Client::new(&token, Handler).unwrap(); + /// + /// client.start().unwrap(); /// ``` /// /// # Errors diff --git a/src/model/channel/channel_category.rs b/src/model/channel/channel_category.rs index f567cee..73c50a7 100644 --- a/src/model/channel/channel_category.rs +++ b/src/model/channel/channel_category.rs @@ -93,20 +93,14 @@ impl ChannelCategory { } } - let mut map = Map::new(); - map.insert("name".to_string(), Value::String(self.name.clone())); - map.insert( - "position".to_string(), - Value::Number(Number::from(self.position)), - ); - map.insert( - "type".to_string(), - Value::String(self.kind.name().to_string()), - ); + let mut map = HashMap::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 = f(EditChannel(map)).0; + let map = serenity_utils::hashmap_to_json_map(f(EditChannel(map)).0); - http::edit_channel(self.id.0, &edited).map(|channel| { + http::edit_channel(self.id.0, &map).map(|channel| { let GuildChannel { id, category_id, diff --git a/src/model/channel/channel_id.rs b/src/model/channel/channel_id.rs index 2e415dc..158ebcf 100644 --- a/src/model/channel/channel_id.rs +++ b/src/model/channel/channel_id.rs @@ -11,6 +11,8 @@ use builder::{CreateMessage, EditChannel, GetMessages}; use CACHE; #[cfg(feature = "model")] use http::{self, AttachmentType}; +#[cfg(feature = "model")] +use utils; #[cfg(feature = "model")] impl ChannelId { @@ -189,7 +191,9 @@ impl ChannelId { /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html #[inline] pub fn edit<F: FnOnce(EditChannel) -> EditChannel>(&self, f: F) -> Result<GuildChannel> { - http::edit_channel(self.0, &f(EditChannel::default()).0) + let map = utils::hashmap_to_json_map(f(EditChannel::default()).0); + + http::edit_channel(self.0, &map) } /// Edits a [`Message`] in the channel given its Id. @@ -213,9 +217,9 @@ impl ChannelId { /// [`the limit`]: ../builder/struct.CreateMessage.html#method.content pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message> where F: FnOnce(CreateMessage) -> CreateMessage, M: Into<MessageId> { - let map = f(CreateMessage::default()).0; + let msg = f(CreateMessage::default()); - if let Some(content) = map.get("content") { + 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))); @@ -223,19 +227,21 @@ impl ChannelId { } } + let map = utils::hashmap_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().unwrap().channel(*self) } + 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().unwrap().channel(*self) { + if let Some(channel) = CACHE.read().channel(*self) { return Ok(channel); } } @@ -315,13 +321,13 @@ impl ChannelId { }; Some(match channel { - Guild(channel) => channel.read().unwrap().name().to_string(), - Group(channel) => match channel.read().unwrap().name() { + 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().unwrap().name().to_string(), - Private(channel) => channel.read().unwrap().name(), + Category(category) => category.read().name().to_string(), + Private(channel) => channel.read().name(), }) } @@ -445,9 +451,9 @@ impl ChannelId { /// [Send Messages]: permissions/constant.SEND_MESSAGES.html 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 map = f(CreateMessage::default()).0; + let mut msg = f(CreateMessage::default()); - if let Some(content) = map.get("content") { + 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))); @@ -455,7 +461,8 @@ impl ChannelId { } } - let _ = map.remove("embed"); + let _ = msg.0.remove("embed"); + let map = utils::hashmap_to_json_map(msg.0); http::send_files(self.0, files, map) } @@ -481,14 +488,15 @@ impl ChannelId { /// [Send Messages]: permissions/constant.SEND_MESSAGES.html pub fn send_message<F>(&self, f: F) -> Result<Message> where F: FnOnce(CreateMessage) -> CreateMessage { - let CreateMessage(map, reactions) = f(CreateMessage::default()); + let msg = f(CreateMessage::default()); + let map = utils::hashmap_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) = reactions { + if let Some(reactions) = msg.1 { for reaction in reactions { self.create_reaction(message.id, reaction)?; } diff --git a/src/model/channel/embed.rs b/src/model/channel/embed.rs index 435f706..401f10d 100644 --- a/src/model/channel/embed.rs +++ b/src/model/channel/embed.rs @@ -4,6 +4,8 @@ use utils::Colour; use internal::prelude::*; #[cfg(feature = "model")] use builder::CreateEmbed; +#[cfg(feature = "model")] +use utils; /// Represents a rich embed which allows using richer markdown, multiple fields /// and more. This was heavily inspired by [slack's attachments]. @@ -81,15 +83,14 @@ impl Embed { /// let embed = Embed::fake(|e| e /// .title("Embed title") /// .description("Making a basic embed") - /// .field(|f| f - /// .name("A field") - /// .value("Has some content.") - /// .inline(false))); + /// .field("A field", "Has some content.", false)); /// ``` #[inline] pub fn fake<F>(f: F) -> Value where F: FnOnce(CreateEmbed) -> CreateEmbed { - Value::Object(f(CreateEmbed::default()).0) + let map = utils::hashmap_to_json_map(f(CreateEmbed::default()).0); + + Value::Object(map) } } @@ -123,6 +124,24 @@ pub struct EmbedField { pub value: String, } +impl EmbedField { + /// Creates a new embed field. + /// + /// **Note**: Refer to the [`name`] and [`value`] documentation for maximum + /// lengths. + /// + /// [`name`]: #structfield.name + /// [`value`]: #structfield.value + pub fn new<T, U>(name: T, value: U, inline: bool) -> Self + where T: Into<String>, U: Into<String> { + Self { + name: name.into(), + value: value.into(), + inline, + } + } +} + /// Footer information for an embed. #[derive(Clone, Debug, Deserialize)] pub struct EmbedFooter { diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs index b7295fc..76cea47 100644 --- a/src/model/channel/guild_channel.rs +++ b/src/model/channel/guild_channel.rs @@ -116,7 +116,9 @@ impl GuildChannel { } } - http::create_invite(self.id.0, &f(CreateInvite::default()).0) + let map = serenity_utils::hashmap_to_json_map(f(CreateInvite::default()).0); + + http::create_invite(self.id.0, &map) } /// Creates a [permission overwrite][`PermissionOverwrite`] for either a @@ -157,12 +159,12 @@ impl GuildChannel { /// kind: PermissionOverwriteType::Member(user_id), /// }; /// - /// let cache = CACHE.read().unwrap(); + /// let cache = CACHE.read(); /// let channel = cache /// .guild_channel(channel_id) /// .ok_or(ModelError::ItemMissing)?; /// - /// channel.read().unwrap().create_permission(&overwrite)?; + /// channel.read().create_permission(&overwrite)?; /// # Ok(()) /// # } /// # @@ -199,12 +201,12 @@ impl GuildChannel { /// kind: PermissionOverwriteType::Member(user_id), /// }; /// - /// let cache = CACHE.read().unwrap(); + /// let cache = CACHE.read(); /// let channel = cache /// .guild_channel(channel_id) /// .ok_or(ModelError::ItemMissing)?; /// - /// channel.read().unwrap().create_permission(&overwrite)?; + /// channel.read().create_permission(&overwrite)?; /// # Ok(()) /// # } /// # @@ -312,18 +314,12 @@ impl GuildChannel { } } - let mut map = Map::new(); - map.insert("name".to_string(), Value::String(self.name.clone())); - map.insert( - "position".to_string(), - Value::Number(Number::from(self.position)), - ); - map.insert( - "type".to_string(), - Value::String(self.kind.name().to_string()), - ); + let mut map = HashMap::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 = f(EditChannel(map)).0; + let edited = serenity_utils::hashmap_to_json_map(f(EditChannel(map)).0); match http::edit_channel(self.id.0, &edited) { Ok(channel) => { @@ -365,7 +361,7 @@ impl GuildChannel { /// **Note**: Right now this performs a clone of the guild. This will be /// optimized in the future. #[cfg(feature = "cache")] - pub fn guild(&self) -> Option<Arc<RwLock<Guild>>> { CACHE.read().unwrap().guild(self.guild_id) } + pub fn guild(&self) -> Option<Arc<RwLock<Guild>>> { CACHE.read().guild(self.guild_id) } /// Gets all of the channel's invites. /// @@ -435,18 +431,20 @@ impl GuildChannel { /// use serenity::CACHE; /// /// impl EventHandler for Handler { - /// fn on_message(&self, _: Context, msg: Message) { - /// let channel = match CACHE.read().unwrap().guild_channel(msg.channel_id) { + /// fn message(&self, _: Context, msg: Message) { + /// let channel = match CACHE.read().guild_channel(msg.channel_id) { /// Some(channel) => channel, /// None => return, /// }; /// - /// let permissions = channel.read().unwrap().permissions_for(&msg.author).unwrap(); + /// let permissions = channel.read().permissions_for(&msg.author).unwrap(); /// /// println!("The user's permissions: {:?}", permissions); /// } /// } - /// let mut client = Client::new("token", Handler); client.start().unwrap(); + /// let mut client = Client::new("token", Handler).unwrap(); + /// + /// client.start().unwrap(); /// ``` /// /// Check if the current user has the [Attach Files] and [Send Messages] @@ -463,15 +461,15 @@ impl GuildChannel { /// struct Handler; /// /// impl EventHandler for Handler { - /// fn on_message(&self, _: Context, msg: Message) { - /// let channel = match CACHE.read().unwrap().guild_channel(msg.channel_id) { + /// fn message(&self, _: Context, msg: Message) { + /// let channel = match CACHE.read().guild_channel(msg.channel_id) { /// Some(channel) => channel, /// None => return, /// }; /// - /// let current_user_id = CACHE.read().unwrap().user.id; + /// let current_user_id = CACHE.read().user.id; /// let permissions = - /// channel.read().unwrap().permissions_for(current_user_id).unwrap(); + /// channel.read().permissions_for(current_user_id).unwrap(); /// /// if !permissions.contains(Permissions::ATTACH_FILES | /// Permissions::SEND_MESSAGES) { @@ -492,7 +490,9 @@ impl GuildChannel { /// } /// } /// - /// let mut client = Client::new("token", Handler); client.start().unwrap(); + /// let mut client = Client::new("token", Handler).unwrap(); + /// + /// client.start().unwrap(); /// ``` /// /// # Errors @@ -512,7 +512,7 @@ impl GuildChannel { 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().unwrap().permissions_in(self.id, user_id)) + .map(|g| g.read().permissions_in(self.id, user_id)) } /// Pins a [`Message`] to the channel. diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs index 480c15d..984e274 100644 --- a/src/model/channel/message.rs +++ b/src/model/channel/message.rs @@ -14,6 +14,8 @@ use constants; use CACHE; #[cfg(feature = "model")] use http; +#[cfg(feature = "model")] +use utils as serenity_utils; /// A representation of a message over a guild's text channel, a group, or a /// private channel. @@ -86,7 +88,7 @@ impl Message { /// # struct Handler; /// # /// # impl EventHandler for Handler {} - /// # let mut client = Client::new("token", Handler); + /// # let mut client = Client::new("token", Handler).unwrap(); /// # /// use serenity::model::Channel; /// use serenity::framework::StandardFramework; @@ -97,12 +99,12 @@ impl Message { /// /// command!(channel_name(_ctx, msg) { /// let _ = match msg.channel() { - /// Some(Channel::Category(c)) => msg.reply(&c.read().unwrap().name), - /// Some(Channel::Group(c)) => msg.reply(&c.read().unwrap().name()), - /// Some(Channel::Guild(c)) => msg.reply(&c.read().unwrap().name), + /// Some(Channel::Category(c)) => msg.reply(&c.read().name), + /// Some(Channel::Group(c)) => msg.reply(&c.read().name()), + /// Some(Channel::Guild(c)) => msg.reply(&c.read().name), /// Some(Channel::Private(c)) => { - /// let channel = c.read().unwrap(); - /// let user = channel.recipient.read().unwrap(); + /// let channel = c.read(); + /// let user = channel.recipient.read(); /// /// msg.reply(&format!("DM with {}", user.name.clone())) /// }, @@ -113,12 +115,12 @@ impl Message { /// ``` #[cfg(feature = "cache")] #[inline] - pub fn channel(&self) -> Option<Channel> { CACHE.read().unwrap().channel(self.channel_id) } + pub fn channel(&self) -> Option<Channel> { CACHE.read().channel(self.channel_id) } /// A util function for determining whether this message was sent by someone else, or the /// bot. #[cfg(all(feature = "cache", feature = "utils"))] - pub fn is_own(&self) -> bool { self.author.id == CACHE.read().unwrap().user.id } + pub fn is_own(&self) -> bool { self.author.id == CACHE.read().user.id } /// Deletes the message. /// @@ -138,7 +140,7 @@ impl Message { #[cfg(feature = "cache")] { let req = Permissions::MANAGE_MESSAGES; - let is_author = self.author.id == CACHE.read().unwrap().user.id; + let is_author = self.author.id == CACHE.read().user.id; let has_perms = utils::user_has_perms(self.channel_id, req)?; if !is_author && !has_perms { @@ -211,7 +213,7 @@ impl Message { where F: FnOnce(CreateMessage) -> CreateMessage { #[cfg(feature = "cache")] { - if self.author.id != CACHE.read().unwrap().user.id { + if self.author.id != CACHE.read().user.id { return Err(Error::Model(ModelError::InvalidUser)); } } @@ -230,7 +232,7 @@ impl Message { builder = builder.tts(true); } - let map = f(builder).0; + let map = serenity_utils::hashmap_to_json_map(f(builder).0); match http::edit_message(self.channel_id.0, self.id.0, &Value::Object(map)) { Ok(edited) => { @@ -335,7 +337,7 @@ impl Message { #[cfg(feature = "cache")] pub fn guild(&self) -> Option<Arc<RwLock<Guild>>> { self.guild_id() - .and_then(|guild_id| CACHE.read().unwrap().guild(guild_id)) + .and_then(|guild_id| CACHE.read().guild(guild_id)) } /// Retrieves the Id of the guild that the message was sent in, if sent in @@ -345,8 +347,8 @@ impl Message { /// cache. #[cfg(feature = "cache")] pub fn guild_id(&self) -> Option<GuildId> { - match CACHE.read().unwrap().channel(self.channel_id) { - Some(Channel::Guild(ch)) => Some(ch.read().unwrap().guild_id), + match CACHE.read().channel(self.channel_id) { + Some(Channel::Guild(ch)) => Some(ch.read().guild_id), _ => None, } } @@ -354,7 +356,7 @@ impl Message { /// True if message was sent using direct messages. #[cfg(feature = "cache")] pub fn is_private(&self) -> bool { - match CACHE.read().unwrap().channel(self.channel_id) { + match CACHE.read().channel(self.channel_id) { Some(Channel::Group(_)) | Some(Channel::Private(_)) => true, _ => false, } diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 07a73d7..ae46805 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -84,16 +84,16 @@ impl Channel { pub fn delete(&self) -> Result<()> { match *self { Channel::Group(ref group) => { - let _ = group.read().unwrap().leave()?; + let _ = group.read().leave()?; }, Channel::Guild(ref public_channel) => { - let _ = public_channel.read().unwrap().delete()?; + let _ = public_channel.read().delete()?; }, Channel::Private(ref private_channel) => { - let _ = private_channel.read().unwrap().delete()?; + let _ = private_channel.read().delete()?; }, Channel::Category(ref category) => { - category.read().unwrap().delete()?; + category.read().delete()?; }, } @@ -386,15 +386,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().unwrap().name(), f), - Channel::Guild(ref ch) => Display::fmt(&ch.read().unwrap().id.mention(), f), + Channel::Group(ref group) => Display::fmt(&group.read().name(), f), + Channel::Guild(ref ch) => Display::fmt(&ch.read().id.mention(), f), Channel::Private(ref ch) => { - let channel = ch.read().unwrap(); - let recipient = channel.recipient.read().unwrap(); + let channel = ch.read(); + let recipient = channel.recipient.read(); Display::fmt(&recipient.name, f) }, - Channel::Category(ref category) => Display::fmt(&category.read().unwrap().name, f), + Channel::Category(ref category) => Display::fmt(&category.read().name, f), } } } diff --git a/src/model/channel/private_channel.rs b/src/model/channel/private_channel.rs index d6ba781..01b387a 100644 --- a/src/model/channel/private_channel.rs +++ b/src/model/channel/private_channel.rs @@ -279,6 +279,6 @@ impl PrivateChannel { 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().unwrap().name) + f.write_str(&self.recipient.read().name) } } diff --git a/src/model/channel/reaction.rs b/src/model/channel/reaction.rs index 8edc2e9..1a40ecb 100644 --- a/src/model/channel/reaction.rs +++ b/src/model/channel/reaction.rs @@ -48,7 +48,7 @@ impl Reaction { pub fn delete(&self) -> Result<()> { let user_id = feature_cache! { { - let user = if self.user_id == CACHE.read().unwrap().user.id { + let user = if self.user_id == CACHE.read().user.id { None } else { Some(self.user_id.0) diff --git a/src/model/error.rs b/src/model/error.rs index 454b94b..c2f568b 100644 --- a/src/model/error.rs +++ b/src/model/error.rs @@ -27,7 +27,7 @@ use super::Permissions; /// struct Handler; /// /// impl EventHandler for Handler { -/// fn on_guild_ban_removal(&self, context: Context, guild_id: GuildId, user: User) { +/// fn guild_ban_removal(&self, context: Context, guild_id: GuildId, user: User) { /// // If the user has an even discriminator, don't re-ban them. /// if user.discriminator % 2 == 0 { /// return; @@ -46,8 +46,10 @@ use super::Permissions; /// } /// } /// } -/// let token = env::var("DISCORD_TOKEN")?; -/// let mut client = Client::new(&token, Handler); client.start()?; +/// let token = env::var("DISCORD_BOT_TOKEN")?; +/// let mut client = Client::new(&token, Handler).unwrap(); +/// +/// client.start()?; /// # Ok(()) /// # } /// # diff --git a/src/model/event.rs b/src/model/event.rs index d4148b9..aea05b1 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -61,7 +61,7 @@ impl CacheUpdate for ChannelCreateEvent { let channel_id = group.with_mut(|writer| { for (recipient_id, recipient) in &mut writer.recipients { - cache.update_user_entry(&recipient.read().unwrap()); + cache.update_user_entry(&recipient.read()); *recipient = Arc::clone(&cache.users[recipient_id]); } @@ -110,7 +110,7 @@ impl CacheUpdate for ChannelCreateEvent { }, Channel::Category(ref category) => cache .categories - .insert(category.read().unwrap().id, Arc::clone(category)) + .insert(category.read().id, Arc::clone(category)) .map(Channel::Category), } } @@ -214,7 +214,7 @@ impl CacheUpdate for ChannelRecipientAddEvent { let user = Arc::clone(&cache.users[&self.user.id]); cache.groups.get_mut(&self.channel_id).map(|group| { - group.write().unwrap().recipients.insert(self.user.id, user); + group.write().recipients.insert(self.user.id, user); }); None @@ -263,16 +263,16 @@ impl CacheUpdate for ChannelUpdateEvent { e.insert(Arc::clone(group)); }, Entry::Occupied(mut e) => { - let mut dest = e.get_mut().write().unwrap(); + let mut dest = e.get_mut().write(); if no_recipients { let recipients = mem::replace(&mut dest.recipients, HashMap::new()); - dest.clone_from(&group.read().unwrap()); + dest.clone_from(&group.read()); dest.recipients = recipients; } else { - dest.clone_from(&group.read().unwrap()); + dest.clone_from(&group.read()); } }, } @@ -289,13 +289,13 @@ impl CacheUpdate for ChannelUpdateEvent { Channel::Private(ref channel) => { cache .private_channels - .get_mut(&channel.read().unwrap().id) + .get_mut(&channel.read().id) .map(|private| private.clone_from(channel)); }, Channel::Category(ref category) => { cache .categories - .get_mut(&category.read().unwrap().id) + .get_mut(&category.read().id) .map(|c| c.clone_from(category)); }, } @@ -340,7 +340,7 @@ impl CacheUpdate for GuildCreateEvent { let mut guild = self.guild.clone(); for (user_id, member) in &mut guild.members { - cache.update_user_entry(&member.user.read().unwrap()); + cache.update_user_entry(&member.user.read()); let user = Arc::clone(&cache.users[user_id]); member.user = Arc::clone(&user); @@ -375,7 +375,7 @@ impl CacheUpdate for GuildDeleteEvent { 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().unwrap().channels.keys() { + for channel_id in guild.write().channels.keys() { cache.channels.remove(channel_id); } @@ -430,7 +430,7 @@ impl CacheUpdate for GuildMemberAddEvent { 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().unwrap()); + cache.update_user_entry(&self.member.user.read()); // Always safe due to being inserted above. self.member.user = Arc::clone(&cache.users[&user_id]); @@ -500,7 +500,7 @@ impl CacheUpdate for GuildMemberUpdateEvent { cache.update_user_entry(&self.user); if let Some(guild) = cache.guilds.get_mut(&self.guild_id) { - let mut guild = guild.write().unwrap(); + let mut guild = guild.write(); let mut found = false; @@ -509,7 +509,7 @@ impl CacheUpdate for GuildMemberUpdateEvent { member.nick.clone_from(&self.nick); member.roles.clone_from(&self.roles); - member.user.write().unwrap().clone_from(&self.user); + member.user.write().clone_from(&self.user); found = true; @@ -552,7 +552,7 @@ impl CacheUpdate for GuildMembersChunkEvent { fn update(&mut self, cache: &mut Cache) -> Option<()> { for member in self.members.values() { - cache.update_user_entry(&member.user.read().unwrap()); + cache.update_user_entry(&member.user.read()); } cache.guilds.get_mut(&self.guild_id).map(|guild| { @@ -609,7 +609,6 @@ impl CacheUpdate for GuildRoleCreateEvent { cache.guilds.get_mut(&self.guild_id).map(|guild| { guild .write() - .unwrap() .roles .insert(self.role.id, self.role.clone()) }); @@ -685,7 +684,7 @@ impl CacheUpdate for GuildUpdateEvent { fn update(&mut self, cache: &mut Cache) -> Option<()> { cache.guilds.get_mut(&self.guild.id).map(|guild| { - let mut guild = guild.write().unwrap(); + let mut guild = guild.write(); guild.afk_timeout = self.guild.afk_timeout; guild.afk_channel_id.clone_from(&self.guild.afk_channel_id); @@ -768,13 +767,13 @@ impl CacheUpdate for PresenceUpdateEvent { let user_id = self.presence.user_id; if let Some(user) = self.presence.user.as_mut() { - cache.update_user_entry(&user.read().unwrap()); + 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().unwrap(); + let mut guild = guild.write(); // If the member went offline, remove them from the presence list. if self.presence.status == OnlineStatus::Offline { @@ -920,7 +919,7 @@ impl CacheUpdate for ReadyEvent { for (user_id, presence) in &mut ready.presences { if let Some(ref user) = presence.user { - cache.update_user_entry(&user.read().unwrap()); + cache.update_user_entry(&user.read()); } presence.user = cache.users.get(user_id).cloned(); @@ -1004,7 +1003,7 @@ impl CacheUpdate for VoiceStateUpdateEvent { 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().unwrap(); + let mut guild = guild.write(); if self.voice_state.channel_id.is_some() { // Update or add to the voice state list @@ -1062,7 +1061,8 @@ pub enum GatewayEvent { Dispatch(u64, Event), Heartbeat(u64), Reconnect, - InvalidateSession, + /// Whether the session can be resumed. + InvalidateSession(bool), Hello(u64), HeartbeatAck, } @@ -1098,7 +1098,15 @@ impl GatewayEvent { GatewayEvent::Heartbeat(s) }, OpCode::Reconnect => GatewayEvent::Reconnect, - OpCode::InvalidSession => GatewayEvent::InvalidateSession, + OpCode::InvalidSession => { + let resumable = map.remove("d") + .ok_or_else(|| { + DeError::custom("expected gateway invalid session d") + }) + .and_then(bool::deserialize)?; + + GatewayEvent::InvalidateSession(resumable) + }, OpCode::Hello => { let mut d = map.remove("d") .ok_or_else(|| DeError::custom("expected gateway hello d")) diff --git a/src/model/gateway.rs b/src/model/gateway.rs index 4edc20e..ca846e8 100644 --- a/src/model/gateway.rs +++ b/src/model/gateway.rs @@ -1,6 +1,7 @@ +use parking_lot::RwLock; use serde::de::Error as DeError; use serde_json; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; use super::utils::*; use super::*; diff --git a/src/model/guild/audit_log.rs b/src/model/guild/audit_log.rs index d019b61..b3caa73 100644 --- a/src/model/guild/audit_log.rs +++ b/src/model/guild/audit_log.rs @@ -1,4 +1,4 @@ -use super::super::{AuditLogEntryId, UserId}; +use super::super::{AuditLogEntryId, User, UserId, ChannelId, Webhook}; use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor}; use std::fmt; use std::collections::HashMap; @@ -6,7 +6,7 @@ use std::mem::transmute; /// Determines to what entity an action was used on. #[derive(Debug)] -#[repr(i32)] +#[repr(u8)] pub enum Target { Guild = 10, Channel = 20, @@ -28,10 +28,11 @@ pub enum Action { Invite(ActionInvite), Webhook(ActionWebhook), Emoji(ActionEmoji), + MessageDelete, } #[derive(Debug)] -#[repr(i32)] +#[repr(u8)] pub enum ActionChannel { Create = 10, Update = 11, @@ -39,7 +40,7 @@ pub enum ActionChannel { } #[derive(Debug)] -#[repr(i32)] +#[repr(u8)] pub enum ActionChannelOverwrite { Create = 13, Update = 14, @@ -47,7 +48,7 @@ pub enum ActionChannelOverwrite { } #[derive(Debug)] -#[repr(i32)] +#[repr(u8)] pub enum ActionMember { Kick = 20, Prune = 21, @@ -58,7 +59,7 @@ pub enum ActionMember { } #[derive(Debug)] -#[repr(i32)] +#[repr(u8)] pub enum ActionRole { Create = 30, Update = 31, @@ -66,7 +67,7 @@ pub enum ActionRole { } #[derive(Debug)] -#[repr(i32)] +#[repr(u8)] pub enum ActionInvite { Create = 40, Update = 41, @@ -74,7 +75,7 @@ pub enum ActionInvite { } #[derive(Debug)] -#[repr(i32)] +#[repr(u8)] pub enum ActionWebhook { Create = 50, Update = 51, @@ -82,7 +83,7 @@ pub enum ActionWebhook { } #[derive(Debug)] -#[repr(i32)] +#[repr(u8)] pub enum ActionEmoji { Create = 60, Delete = 61, @@ -92,6 +93,7 @@ pub enum ActionEmoji { #[derive(Debug, Deserialize)] pub struct Change { #[serde(rename = "key")] pub name: String, + // TODO: Change these to an actual type. #[serde(rename = "old_value")] pub old: String, #[serde(rename = "new_value")] pub new: String, } @@ -99,6 +101,8 @@ pub struct Change { #[derive(Debug)] pub struct AuditLogs { pub entries: HashMap<AuditLogEntryId, AuditLogEntry>, + pub webhooks: Vec<Webhook>, + pub users: Vec<User>, } #[derive(Debug, Deserialize)] @@ -106,8 +110,7 @@ pub struct AuditLogEntry { /// Determines to what entity an [`action`] was used on. /// /// [`action`]: #structfield.action - #[serde(deserialize_with = "deserialize_target", rename = "target_type")] - pub target: Target, + pub target_id: u64, /// Determines what action was done on a [`target`] /// /// [`target`]: #structfield.target @@ -118,30 +121,30 @@ pub struct AuditLogEntry { /// The user that did this action on a target. pub user_id: UserId, /// What changes were made. - pub changes: Vec<Change>, + pub changes: Option<Vec<Change>>, /// The id of this entry. pub id: AuditLogEntryId, + /// Some optional data assosiated with this entry. + pub options: Option<Options>, } -fn deserialize_target<'de, D: Deserializer<'de>>(de: D) -> Result<Target, D::Error> { - struct TargetVisitor; - - impl<'de> Visitor<'de> for TargetVisitor { - type Value = Target; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an integer between 0 to 70") - } - - fn visit_i32<E: de::Error>(self, value: i32) -> Result<Target, E> { - Ok(match value { - 10...70 => unsafe { transmute(value) }, - _ => return Err(E::custom(format!("unexpected target number: {}", value))), - }) - } - } +#[derive(Debug, Deserialize)] +pub struct Options { + /// Number of days after which inactive members were kicked. + pub delete_member_days: String, + /// Number of members removed by the prune + pub members_removed: String, + /// Channel in which the messages were deleted + pub channel_id: ChannelId, + /// Number of deleted messages. + pub count: u32, + /// Id of the overwritten entity + pub id: u64, + /// Type of overwritten entity ("member" or "role"). + #[serde(rename = "type")] pub kind: String, + /// Name of the role if type is "role" + pub role_name: String, - de.deserialize_i32(TargetVisitor) } fn deserialize_action<'de, D: Deserializer<'de>>(de: D) -> Result<Action, D::Error> { @@ -151,10 +154,10 @@ fn deserialize_action<'de, D: Deserializer<'de>>(de: D) -> Result<Action, D::Err type Value = Action; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an integer between 1 to 62") + formatter.write_str("an integer between 1 to 72") } - fn visit_i32<E: de::Error>(self, value: i32) -> Result<Action, E> { + fn visit_u8<E: de::Error>(self, value: u8) -> Result<Action, E> { Ok(match value { 1 => Action::GuildUpdate, 10...12 => Action::Channel(unsafe { transmute(value) }), @@ -164,12 +167,13 @@ fn deserialize_action<'de, D: Deserializer<'de>>(de: D) -> Result<Action, D::Err 40...42 => Action::Invite(unsafe { transmute(value) }), 50...52 => Action::Webhook(unsafe { transmute(value) }), 60...62 => Action::Emoji(unsafe { transmute(value) }), + 72 => Action::MessageDelete, _ => return Err(E::custom(format!("Unexpected action number: {}", value))), }) } } - de.deserialize_i32(ActionVisitor) + de.deserialize_u8(ActionVisitor) } impl<'de> Deserialize<'de> for AuditLogs { @@ -178,6 +182,8 @@ impl<'de> Deserialize<'de> for AuditLogs { #[serde(field_identifier)] enum Field { #[serde(rename = "audit_log_entries")] Entries, + #[serde(rename = "webhooks")] Webhooks, + #[serde(rename = "users")] Users, } struct EntriesVisitor; @@ -190,22 +196,49 @@ impl<'de> Deserialize<'de> for AuditLogs { } fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<AuditLogs, V::Error> { - let audit_log_entries = loop { - if let Some(Field::Entries) = map.next_key()? { - break map.next_value::<Vec<AuditLogEntry>>()?; + let mut audit_log_entries = None; + let mut users = None; + let mut webhooks = None; + + while let Some(field) = map.next_key()? { + match field { + Field::Entries => { + if audit_log_entries.is_some() { + return Err(de::Error::duplicate_field("entries")); + } + + audit_log_entries = Some(map.next_value::<Vec<AuditLogEntry>>()?); + }, + Field::Webhooks => { + if webhooks.is_some() { + return Err(de::Error::duplicate_field("webhooks")); + } + + webhooks = Some(map.next_value::<Vec<Webhook>>()?); + }, + Field::Users => { + if users.is_some() { + return Err(de::Error::duplicate_field("users")); + } + + users = Some(map.next_value::<Vec<User>>()?); + }, } - }; + } Ok(AuditLogs { entries: audit_log_entries + .unwrap() .into_iter() .map(|entry| (entry.id, entry)) .collect(), + webhooks: webhooks.unwrap(), + users: users.unwrap(), }) } } - const FIELD: &'static [&'static str] = &["audit_log_entries"]; + const FIELD: &[&str] = &["audit_log_entries"]; de.deserialize_struct("AuditLogs", FIELD, EntriesVisitor) } } diff --git a/src/model/guild/emoji.rs b/src/model/guild/emoji.rs index 4a2190f..7f332ae 100644 --- a/src/model/guild/emoji.rs +++ b/src/model/guild/emoji.rs @@ -151,8 +151,8 @@ impl Emoji { /// ``` #[cfg(feature = "cache")] pub fn find_guild_id(&self) -> Option<GuildId> { - for guild in CACHE.read().unwrap().guilds.values() { - let guild = guild.read().unwrap(); + for guild in CACHE.read().guilds.values() { + let guild = guild.read(); if guild.emojis.contains_key(&self.id) { return Some(guild.id); diff --git a/src/model/guild/feature.rs b/src/model/guild/feature.rs deleted file mode 100644 index 40fd3fd..0000000 --- a/src/model/guild/feature.rs +++ /dev/null @@ -1,25 +0,0 @@ -/// A special feature, such as for VIP guilds, that a [`Guild`] has had granted -/// to them. -/// -/// [`Guild`]: struct.Guild.html -#[derive(Copy, Clone, Debug, Deserialize, Hash, Eq, PartialEq)] -pub enum Feature { - /// The [`Guild`] can set a custom [`splash`][`Guild::splash`] image on - /// invite URLs. - /// - /// [`Guild`]: struct.Guild.html - /// [`Guild::splash`]: struct.Guild.html#structfield.splash - #[serde(rename = "INVITE_SPLASH")] - InviteSplash, - /// The [`Guild`] can set a Vanity URL, which is a custom-named permanent - /// invite code. - /// - /// [`Guild`]: struct.Guild.html - #[serde(rename = "VANITY_URL")] - VanityUrl, - /// The [`Guild`] has access to VIP voice channel regions. - /// - /// [`Guild`]: struct.Guild.html - #[serde(rename = "VIP_REGIONS")] - VipRegions, -} diff --git a/src/model/guild/guild_id.rs b/src/model/guild/guild_id.rs index 622d059..37b3886 100644 --- a/src/model/guild/guild_id.rs +++ b/src/model/guild/guild_id.rs @@ -5,11 +5,11 @@ use CACHE; #[cfg(feature = "model")] use builder::{EditGuild, EditMember, EditRole}; #[cfg(feature = "model")] -use http; -#[cfg(feature = "model")] use internal::prelude::*; #[cfg(feature = "model")] use model::guild::BanOptions; +#[cfg(feature = "model")] +use {http, utils}; #[cfg(feature = "model")] impl GuildId { @@ -73,7 +73,12 @@ impl GuildId { /// Gets a list of the guild's audit log entries #[inline] - pub fn audit_logs(&self) -> Result<AuditLogs> { http::get_audit_logs(self.0) } + 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. /// @@ -168,7 +173,9 @@ impl GuildId { /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html #[inline] pub fn create_role<F: FnOnce(EditRole) -> EditRole>(&self, f: F) -> Result<Role> { - http::create_role(self.0, &f(EditRole::default()).0) + let map = utils::hashmap_to_json_map(f(EditRole::default()).0); + + http::create_role(self.0, &map) } /// Deletes the current guild if the current account is the owner of the @@ -229,7 +236,9 @@ impl GuildId { /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html #[inline] pub fn edit<F: FnOnce(EditGuild) -> EditGuild>(&mut self, f: F) -> Result<PartialGuild> { - http::edit_guild(self.0, &f(EditGuild::default()).0) + let map = utils::hashmap_to_json_map(f(EditGuild::default()).0); + + http::edit_guild(self.0, &map) } /// Edits an [`Emoji`]'s name in the guild. @@ -266,7 +275,9 @@ impl GuildId { #[inline] pub fn edit_member<F, U>(&self, user_id: U, f: F) -> Result<()> where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> { - http::edit_member(self.0, user_id.into().0, &f(EditMember::default()).0) + let map = utils::hashmap_to_json_map(f(EditMember::default()).0); + + http::edit_member(self.0, user_id.into().0, &map) } /// Edits the current user's nickname for the guild. @@ -300,7 +311,9 @@ impl GuildId { #[inline] pub fn edit_role<F, R>(&self, role_id: R, f: F) -> Result<Role> where F: FnOnce(EditRole) -> EditRole, R: Into<RoleId> { - http::edit_role(self.0, role_id.into().0, &f(EditRole::default()).0) + let map = utils::hashmap_to_json_map(f(EditRole::default()).0); + + http::edit_role(self.0, role_id.into().0, &map) } /// Edits the order of [`Role`]s @@ -326,7 +339,7 @@ impl GuildId { /// Search the cache for the guild. #[cfg(feature = "cache")] - pub fn find(&self) -> Option<Arc<RwLock<Guild>>> { CACHE.read().unwrap().guild(*self) } + pub fn find(&self) -> Option<Arc<RwLock<Guild>>> { CACHE.read().guild(*self) } /// Requests the guild over REST. /// @@ -429,7 +442,7 @@ impl GuildId { /// [`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().unwrap().shard_count) } + 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. /// diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs index c81e2a3..e5feee7 100644 --- a/src/model/guild/member.rs +++ b/src/model/guild/member.rs @@ -6,15 +6,13 @@ use model::*; #[cfg(feature = "model")] use std::borrow::Cow; #[cfg(all(feature = "cache", feature = "model"))] -use CACHE; -#[cfg(all(feature = "cache", feature = "model"))] use internal::prelude::*; -#[cfg(all(feature = "cache", feature = "model"))] -use http; #[cfg(all(feature = "builder", feature = "cache", feature = "model"))] use builder::EditMember; #[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 { @@ -89,7 +87,7 @@ impl Member { return Ok(()); } - match http::add_member_role(self.guild_id.0, self.user.read().unwrap().id.0, role_id.0) { + match http::add_member_role(self.guild_id.0, self.user.read().id.0, role_id.0) { Ok(()) => { self.roles.push(role_id); @@ -110,9 +108,10 @@ impl Member { pub fn add_roles(&mut self, role_ids: &[RoleId]) -> Result<()> { self.roles.extend_from_slice(role_ids); - let map = EditMember::default().roles(&self.roles).0; + let builder = EditMember::default().roles(&self.roles); + let map = utils::hashmap_to_json_map(builder.0); - match http::edit_member(self.guild_id.0, self.user.read().unwrap().id.0, &map) { + 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)); @@ -150,7 +149,7 @@ impl Member { http::ban_user( self.guild_id.0, - self.user.read().unwrap().id.0, + self.user.read().id.0, dmd, &*reason, ) @@ -159,8 +158,8 @@ impl Member { /// Determines the member's colour. #[cfg(all(feature = "cache", feature = "utils"))] pub fn colour(&self) -> Option<Colour> { - let cache = CACHE.read().unwrap(); - let guild = try_opt!(cache.guilds.get(&self.guild_id)).read().unwrap(); + let cache = CACHE.read(); + let guild = try_opt!(cache.guilds.get(&self.guild_id)).read(); let mut roles = self.roles .iter() @@ -176,6 +175,27 @@ impl Member { .map(|r| r.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(); + + for (cid, channel) in &reader.channels { + if reader.permissions_for(*cid, self.user.read().id).read_messages() { + return Some(channel.clone()); + } + } + + None + } + /// Calculates the member's display name. /// /// The nickname takes priority over the member's username if it exists. @@ -184,7 +204,7 @@ impl Member { self.nick .as_ref() .map(Cow::Borrowed) - .unwrap_or_else(|| Cow::Owned(self.user.read().unwrap().name.clone())) + .unwrap_or_else(|| Cow::Owned(self.user.read().name.clone())) } /// Returns the DiscordTag of a Member, taking possible nickname into account. @@ -193,7 +213,7 @@ impl Member { format!( "{}#{}", self.display_name(), - self.user.read().unwrap().discriminator + self.user.read().discriminator ) } @@ -207,9 +227,9 @@ impl Member { /// [`EditMember`]: ../builder/struct.EditMember.html #[cfg(feature = "cache")] pub fn edit<F: FnOnce(EditMember) -> EditMember>(&self, f: F) -> Result<()> { - let map = f(EditMember::default()).0; + let map = utils::hashmap_to_json_map(f(EditMember::default()).0); - http::edit_member(self.guild_id.0, self.user.read().unwrap().id.0, &map) + http::edit_member(self.guild_id.0, self.user.read().id.0, &map) } /// Kick the member from the guild. @@ -252,17 +272,16 @@ impl Member { let has_perms = CACHE .read() - .unwrap() .guilds .get(&self.guild_id) - .map(|guild| guild.read().unwrap().has_perms(req)); + .map(|guild| guild.read().has_perms(req)); if let Some(false) = has_perms { return Err(Error::Model(ModelError::InvalidPermissions(req))); } } - self.guild_id.kick(self.user.read().unwrap().id) + self.guild_id.kick(self.user.read().id) } /// Returns the guild-level permissions for the member. @@ -312,7 +331,7 @@ impl Member { return Ok(()); } - match http::remove_member_role(self.guild_id.0, self.user.read().unwrap().id.0, role_id.0) { + 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); @@ -332,9 +351,10 @@ impl Member { pub fn remove_roles(&mut self, role_ids: &[RoleId]) -> Result<()> { self.roles.retain(|r| !role_ids.contains(r)); - let map = EditMember::default().roles(&self.roles).0; + let builder = EditMember::default().roles(&self.roles); + let map = utils::hashmap_to_json_map(builder.0); - match http::edit_member(self.guild_id.0, self.user.read().unwrap().id.0, &map) { + 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); @@ -356,7 +376,6 @@ impl Member { .find() .map(|g| g .read() - .unwrap() .roles .values() .filter(|role| self.roles.contains(&role.id)) @@ -378,7 +397,7 @@ impl Member { /// [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().unwrap().id.0) + http::remove_ban(self.guild_id.0, self.user.read().id.0) } } @@ -394,6 +413,6 @@ impl Display for Member { /// // This is in the format of `<@USER_ID>`. fn fmt(&self, f: &mut Formatter) -> FmtResult { - Display::fmt(&self.user.read().unwrap().mention(), f) + Display::fmt(&self.user.read().mention(), f) } } diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index fe9bc6a..ab74109 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -1,5 +1,4 @@ mod emoji; -mod feature; mod guild_id; mod integration; mod member; @@ -8,7 +7,6 @@ mod role; mod audit_log; pub use self::emoji::*; -pub use self::feature::*; pub use self::guild_id::*; pub use self::integration::*; pub use self::member::*; @@ -21,7 +19,6 @@ use serde::de::Error as DeError; use serde_json; use super::utils::*; use model::*; -use std; #[cfg(all(feature = "cache", feature = "model"))] use CACHE; @@ -31,6 +28,8 @@ use http; use builder::{EditGuild, EditMember, EditRole}; #[cfg(feature = "model")] use constants::LARGE_THRESHOLD; +#[cfg(feature = "model")] +use std; /// A representation of a banning of a user. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash)] @@ -62,8 +61,15 @@ pub struct Guild { /// VIP features enabled for the guild. Can be obtained through the /// [Discord Partnership] website. /// + /// The following is a list of known features: + /// + /// - `INVITE_SPLASH` + /// - `VANITY_URL` + /// - `VERIFIED` + /// - `VIP_REGIONS` + /// /// [Discord Partnership]: https://discordapp.com/partners - pub features: Vec<Feature>, + pub features: Vec<String>, /// The hash of the icon used by the guild. /// /// In the client, this appears on the guild list on the left-hand side. @@ -124,13 +130,10 @@ pub struct Guild { #[cfg(feature = "model")] impl Guild { - #[cfg(feature = "cache")] - /// Returns the "default" channel of the guild. - /// (This returns the first channel that can be read by the bot, if there isn't one, + /// 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) -> Option<GuildChannel> { - let uid = CACHE.read().unwrap().user.id; - + pub fn default_channel(&self, uid: UserId) -> Option<Arc<RwLock<GuildChannel>>> { for (cid, channel) in &self.channels { if self.permissions_in(*cid, uid).read_messages() { return Some(channel.read().unwrap().clone()); @@ -145,7 +148,7 @@ 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<GuildChannel> { + pub fn default_channel_guaranteed(&self) -> Option<Arc<RwLock<GuildChannel>>> { for (cid, channel) in &self.channels { for memid in self.members.keys() { if self.permissions_in(*cid, *memid).read_messages() { @@ -239,7 +242,12 @@ impl Guild { /// /// [`AuditLogs`]: audit_log/struct.AuditLogs.html #[inline] - pub fn audit_logs(&self) -> Result<AuditLogs> { self.id.audit_logs() } + 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) + } /// Gets all of the guild's channels over the REST API. /// @@ -402,7 +410,7 @@ impl Guild { pub fn delete(&self) -> Result<PartialGuild> { #[cfg(feature = "cache")] { - if self.owner_id != CACHE.read().unwrap().user.id { + if self.owner_id != CACHE.read().user.id { let req = Permissions::MANAGE_GUILD; return Err(Error::Model(ModelError::InvalidPermissions(req))); @@ -739,9 +747,9 @@ impl Guild { self.members .values() .find(|member| { - let name_matches = member.user.read().unwrap().name == name; + let name_matches = member.user.read().name == name; let discrim_matches = match discrim { - Some(discrim) => member.user.read().unwrap().discriminator == discrim, + Some(discrim) => member.user.read().discriminator == discrim, None => true, }; @@ -756,11 +764,6 @@ impl Guild { /// Retrieves all [`Member`] that start with a given `String`. /// - /// If the prefix is "zey", following results are possible: - /// - "zey", "zeyla", "zey mei" - /// If 'case_sensitive' is false, the following are not found: - /// - "Zey", "ZEYla", "zeY mei" - /// /// `sorted` decides whether the best early match of the `prefix` /// should be the criteria to sort the result. /// For the `prefix` "zey" and the unsorted result: @@ -775,9 +778,9 @@ impl Guild { .filter(|member| if case_sensitive { - member.user.read().unwrap().name.starts_with(prefix) + member.user.read().name.starts_with(prefix) } else { - starts_with_case_insensitive(&member.user.read().unwrap().name, prefix) + starts_with_case_insensitive(&member.user.read().name, prefix) } || member.nick.as_ref() @@ -794,24 +797,24 @@ impl Guild { .sort_by(|a, b| { let name_a = match a.nick { Some(ref nick) => { - if contains_case_insensitive(&a.user.read().unwrap().name[..], prefix) { - a.user.read().unwrap().name.clone() + if contains_case_insensitive(&a.user.read().name[..], prefix) { + a.user.read().name.clone() } else { nick.clone() } }, - None => a.user.read().unwrap().name.clone(), + None => a.user.read().name.clone(), }; let name_b = match b.nick { Some(ref nick) => { - if contains_case_insensitive(&b.user.read().unwrap().name[..], prefix) { - b.user.read().unwrap().name.clone() + if contains_case_insensitive(&b.user.read().name[..], prefix) { + b.user.read().name.clone() } else { nick.clone() } }, - None => b.user.read().unwrap().name.clone(), + None => b.user.read().name.clone(), }; closest_to_origin(prefix, &name_a[..], &name_b[..]) @@ -850,9 +853,9 @@ impl Guild { .filter(|member| if case_sensitive { - member.user.read().unwrap().name.contains(substring) + member.user.read().name.contains(substring) } else { - contains_case_insensitive(&member.user.read().unwrap().name, substring) + contains_case_insensitive(&member.user.read().name, substring) } || member.nick.as_ref() @@ -870,24 +873,24 @@ impl Guild { .sort_by(|a, b| { let name_a = match a.nick { Some(ref nick) => { - if contains_case_insensitive(&a.user.read().unwrap().name[..], substring) { - a.user.read().unwrap().name.clone() + if contains_case_insensitive(&a.user.read().name[..], substring) { + a.user.read().name.clone() } else { nick.clone() } }, - None => a.user.read().unwrap().name.clone(), + None => a.user.read().name.clone(), }; let name_b = match b.nick { Some(ref nick) => { - if contains_case_insensitive(&b.user.read().unwrap().name[..], substring) { - b.user.read().unwrap().name.clone() + if contains_case_insensitive(&b.user.read().name[..], substring) { + b.user.read().name.clone() } else { nick.clone() } }, - None => b.user.read().unwrap().name.clone(), + None => b.user.read().name.clone(), }; closest_to_origin(substring, &name_a[..], &name_b[..]) @@ -919,17 +922,17 @@ impl Guild { .values() .filter(|member| { if case_sensitive { - member.user.read().unwrap().name.contains(substring) + member.user.read().name.contains(substring) } else { - contains_case_insensitive(&member.user.read().unwrap().name, substring) + contains_case_insensitive(&member.user.read().name, substring) } }).collect(); if sorted { members .sort_by(|a, b| { - let name_a = &a.user.read().unwrap().name; - let name_b = &b.user.read().unwrap().name; + let name_a = &a.user.read().name; + let name_b = &b.user.read().name; closest_to_origin(substring, &name_a[..], &name_b[..]) }); members @@ -978,14 +981,14 @@ impl Guild { Some(ref nick) => { nick.clone() }, - None => a.user.read().unwrap().name.clone(), + None => a.user.read().name.clone(), }; let name_b = match b.nick { Some(ref nick) => { nick.clone() }, - None => b.user.read().unwrap().name.clone(), + None => b.user.read().name.clone(), }; closest_to_origin(substring, &name_a[..], &name_b[..]) @@ -1111,7 +1114,7 @@ impl Guild { } else { warn!( "(╯°□°)╯︵ ┻━┻ {} on {} has non-existent role {:?}", - member.user.read().unwrap().id, + member.user.read().id, self.id, role ); @@ -1124,7 +1127,7 @@ impl Guild { } if let Some(channel) = self.channels.get(&channel_id) { - let channel = channel.read().unwrap(); + let channel = channel.read(); // If this is a text channel, then throw out voice permissions. if channel.kind == ChannelType::Text { @@ -1362,16 +1365,16 @@ impl Guild { /// use serenity::CACHE; /// /// impl EventHandler for Handler { - /// fn on_message(&self, _: Context, msg: Message) { + /// fn message(&self, _: Context, msg: Message) { /// if let Some(arc) = msg.guild_id().unwrap().find() { - /// if let Some(role) = arc.read().unwrap().role_by_name("role_name") { + /// if let Some(role) = arc.read().role_by_name("role_name") { /// println!("{:?}", role); /// } /// } /// } /// } /// - /// let mut client = Client::new("token", Handler); + /// let mut client = Client::new("token", Handler).unwrap(); /// /// client.start().unwrap(); /// ``` @@ -1433,7 +1436,7 @@ impl<'de> Deserialize<'de> for Guild { .map_err(DeError::custom)?; let features = map.remove("features") .ok_or_else(|| DeError::custom("expected guild features")) - .and_then(serde_json::from_value::<Vec<Feature>>) + .and_then(serde_json::from_value::<Vec<String>>) .map_err(DeError::custom)?; let icon = match map.remove("icon") { Some(v) => Option::<String>::deserialize(v).map_err(DeError::custom)?, @@ -1523,11 +1526,13 @@ impl<'de> Deserialize<'de> for Guild { } /// Checks if a `&str` contains another `&str`. +#[cfg(feature = "model")] fn contains_case_insensitive(to_look_at: &str, to_find: &str) -> bool { to_look_at.to_lowercase().contains(to_find) } /// Checks if a `&str` starts with another `&str`. +#[cfg(feature = "model")] fn starts_with_case_insensitive(to_look_at: &str, to_find: &str) -> bool { to_look_at.to_lowercase().starts_with(to_find) } @@ -1539,6 +1544,7 @@ fn starts_with_case_insensitive(to_look_at: &str, to_find: &str) -> bool { /// expected to contain `origin` as substring. /// If not, using `closest_to_origin` would sort these /// the end. +#[cfg(feature = "model")] fn closest_to_origin(origin: &str, word_a: &str, word_b: &str) -> std::cmp::Ordering { let value_a = match word_a.find(origin) { Some(value) => value + word_a.len(), diff --git a/src/model/guild/partial_guild.rs b/src/model/guild/partial_guild.rs index cb2f8ef..c4181fd 100644 --- a/src/model/guild/partial_guild.rs +++ b/src/model/guild/partial_guild.rs @@ -17,7 +17,12 @@ pub struct PartialGuild { pub embed_channel_id: Option<ChannelId>, pub embed_enabled: bool, #[serde(deserialize_with = "deserialize_emojis")] pub emojis: HashMap<EmojiId, Emoji>, - pub features: Vec<Feature>, + /// Features enabled for the guild. + /// + /// Refer to [`Guild::features`] for more information. + /// + /// [`Guild::features`]: struct.Guild.html#structfield.features + pub features: Vec<String>, pub icon: Option<String>, pub mfa_level: u64, pub name: String, @@ -451,7 +456,7 @@ impl PartialGuild { /// use serenity::CACHE; /// /// impl EventHandler for Handler { - /// fn on_message(&self, _: Context, msg: Message) { + /// fn message(&self, _: Context, msg: Message) { /// if let Some(role) = /// msg.guild_id().unwrap().get().unwrap().role_by_name("role_name") { /// println!("Obtained role's reference: {:?}", role); @@ -459,7 +464,7 @@ impl PartialGuild { /// } /// } /// - /// let mut client = Client::new("token", Handler); + /// let mut client = Client::new("token", Handler).unwrap(); /// /// client.start().unwrap(); /// ``` diff --git a/src/model/guild/role.rs b/src/model/guild/role.rs index 7ac7019..3ed718e 100644 --- a/src/model/guild/role.rs +++ b/src/model/guild/role.rs @@ -106,8 +106,8 @@ impl Role { /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound #[cfg(feature = "cache")] pub fn find_guild(&self) -> Result<GuildId> { - for guild in CACHE.read().unwrap().guilds.values() { - let guild = guild.read().unwrap(); + for guild in CACHE.read().guilds.values() { + let guild = guild.read(); if guild.roles.contains_key(&RoleId(self.id.0)) { return Ok(guild.id); @@ -168,10 +168,10 @@ impl RoleId { /// Search the cache for the role. #[cfg(feature = "cache")] pub fn find(&self) -> Option<Role> { - let cache = CACHE.read().unwrap(); + let cache = CACHE.read(); for guild in cache.guilds.values() { - let guild = guild.read().unwrap(); + let guild = guild.read(); if !guild.roles.contains_key(self) { continue; diff --git a/src/model/invite.rs b/src/model/invite.rs index b4f326c..8ef848c 100644 --- a/src/model/invite.rs +++ b/src/model/invite.rs @@ -6,9 +6,9 @@ use super::{Permissions, utils as model_utils}; #[cfg(feature = "model")] use builder::CreateInvite; #[cfg(feature = "model")] -use http; -#[cfg(feature = "model")] use internal::prelude::*; +#[cfg(feature = "model")] +use {http, utils}; /// Information about an invite code. /// @@ -73,7 +73,9 @@ impl Invite { } } - http::create_invite(channel_id.0, &f(CreateInvite::default()).0) + let map = utils::hashmap_to_json_map(f(CreateInvite::default()).0); + + http::create_invite(channel_id.0, &map) } /// Deletes the invite. diff --git a/src/model/misc.rs b/src/model/misc.rs index 625e202..51c304d 100644 --- a/src/model/misc.rs +++ b/src/model/misc.rs @@ -174,7 +174,7 @@ impl FromStr for Role { #[cfg(all(feature = "model", feature = "utils"))] #[derive(Debug)] pub enum RoleIdParseError { - NotPresentInCache, + InvalidFormat, } #[cfg(all(feature = "model", feature = "utils"))] @@ -188,7 +188,7 @@ impl StdError for RoleIdParseError { use self::RoleIdParseError::*; match *self { - NotPresentInCache => "not present in cache", + InvalidFormat => "invalid role id format", } } } @@ -198,9 +198,10 @@ impl FromStr for RoleId { type Err = RoleIdParseError; fn from_str(s: &str) -> StdResult<Self, Self::Err> { - utils::parse_role(s) - .ok_or_else(|| RoleIdParseError::NotPresentInCache) - .map(RoleId) + Ok(match utils::parse_role(s) { + Some(id) => RoleId(id), + None => s.parse::<u64>().map(RoleId).map_err(|_| RoleIdParseError::InvalidFormat)?, + }) } } diff --git a/src/model/mod.rs b/src/model/mod.rs index 0817094..64987e1 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -32,11 +32,12 @@ pub use self::voice::*; pub use self::webhook::*; use chrono::NaiveDateTime; +use parking_lot::RwLock; use self::utils::*; use serde::de::Visitor; use std::collections::HashMap; use std::fmt::{Display, Formatter, Result as FmtResult}; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; use internal::prelude::*; #[cfg(feature = "utils")] @@ -67,7 +68,7 @@ macro_rules! id_u64 { self } } - + impl From<u64> for $name { fn from(id_as_u64: u64) -> $name { $name(id_as_u64) diff --git a/src/model/user.rs b/src/model/user.rs index bf725cc..9f36d0f 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -7,18 +7,22 @@ use model::misc::Mentionable; #[cfg(feature = "model")] use chrono::NaiveDateTime; +#[cfg(all(feature = "cache", feature = "model"))] +use parking_lot::RwLock; #[cfg(feature = "model")] use std::fmt::Write; #[cfg(feature = "model")] use std::mem; #[cfg(all(feature = "cache", feature = "model"))] -use std::sync::{Arc, RwLock}; +use std::sync::Arc; #[cfg(feature = "model")] use builder::{CreateMessage, EditProfile}; #[cfg(all(feature = "cache", feature = "model"))] use CACHE; #[cfg(feature = "model")] use http::{self, GuildPagination}; +#[cfg(feature = "model")] +use utils; /// Information about the current user. #[derive(Clone, Default, Debug, Deserialize)] @@ -46,7 +50,7 @@ impl CurrentUser { /// ```rust,no_run /// # use serenity::CACHE; /// # - /// # let cache = CACHE.read().unwrap(); + /// # let cache = CACHE.read(); /// # /// // assuming the cache has been unlocked /// let user = &cache.user; @@ -80,18 +84,20 @@ impl CurrentUser { /// /// let avatar = serenity::utils::read_image("./avatar.png").unwrap(); /// - /// CACHE.write().unwrap().user.edit(|p| p.avatar(Some(&avatar))); + /// CACHE.write().user.edit(|p| p.avatar(Some(&avatar))); /// ``` pub fn edit<F>(&mut self, f: F) -> Result<()> where F: FnOnce(EditProfile) -> EditProfile { - let mut map = Map::new(); - map.insert("username".to_string(), Value::String(self.name.clone())); + let mut map = HashMap::new(); + map.insert("username", Value::String(self.name.clone())); if let Some(email) = self.email.as_ref() { - map.insert("email".to_string(), Value::String(email.clone())); + map.insert("email", Value::String(email.clone())); } - match http::edit_profile(&f(EditProfile(map)).0) { + let map = utils::hashmap_to_json_map(f(EditProfile(map)).0); + + match http::edit_profile(&map) { Ok(new) => { let _ = mem::replace(self, new); @@ -123,7 +129,7 @@ impl CurrentUser { /// ```rust,no_run /// # use serenity::CACHE; /// # - /// # let cache = CACHE.read().unwrap(); + /// # let cache = CACHE.read(); /// # /// // assuming the cache has been unlocked /// let user = &cache.user; @@ -151,7 +157,7 @@ impl CurrentUser { /// ```rust,no_run /// # use serenity::CACHE; /// # - /// # let mut cache = CACHE.write().unwrap(); + /// # let mut cache = CACHE.write(); /// /// use serenity::model::permissions::Permissions; /// @@ -174,7 +180,7 @@ impl CurrentUser { /// ```rust,no_run /// # use serenity::CACHE; /// # - /// # let mut cache = CACHE.write().unwrap(); + /// # let mut cache = CACHE.write(); /// /// use serenity::model::Permissions; /// @@ -230,7 +236,7 @@ impl CurrentUser { /// ```rust,no_run /// # use serenity::CACHE; /// # - /// # let cache = CACHE.read().unwrap(); + /// # let cache = CACHE.read(); /// # /// // assuming the cache has been unlocked /// let user = &cache.user; @@ -254,7 +260,7 @@ impl CurrentUser { /// ```rust,no_run /// # use serenity::CACHE; /// # - /// # let cache = CACHE.read().unwrap(); + /// # let cache = CACHE.read(); /// # /// // assuming the cache has been unlocked /// println!("The current user's distinct identifier is {}", cache.user.tag()); @@ -424,27 +430,23 @@ impl User { /// struct Handler; /// /// impl EventHandler for Handler { - /// fn on_message(&self, _: Context, msg: Message) { + /// fn message(&self, _: Context, msg: Message) { /// if msg.content == "~help" { - /// let url = match CACHE.read() { - /// Ok(v) => { - /// match v.user.invite_url(Permissions::empty()) { - /// Ok(v) => v, - /// Err(why) => { - /// println!("Error creating invite url: {:?}", why); - /// - /// return; - /// }, - /// } - /// }, + /// let cache = CACHE.read(); + /// + /// let url = match cache.user.invite_url(Permissions::empty()) { + /// Ok(v) => v, /// Err(why) => { - /// println!("Error reading from CACHE: {:?}", why); + /// println!("Error creating invite url: {:?}", why); /// /// return; - /// } + /// }, /// }; - /// let help = format!("Helpful info here. Invite me with this link: <{}>", - /// url); + /// + /// let help = format!( + /// "Helpful info here. Invite me with this link: <{}>", + /// url, + /// ); /// /// match msg.author.direct_message(|m| m.content(&help)) { /// Ok(_) => { @@ -496,12 +498,12 @@ impl User { let private_channel_id = feature_cache! { { let finding = { - let cache = CACHE.read().unwrap(); + let cache = CACHE.read(); let finding = cache.private_channels .values() - .map(|ch| ch.read().unwrap()) - .find(|ch| ch.recipient.read().unwrap().id == self.id) + .map(|ch| ch.read()) + .find(|ch| ch.recipient.read().id == self.id) .map(|ch| ch.id); finding @@ -596,11 +598,10 @@ impl User { GuildContainer::Id(_guild_id) => { feature_cache! {{ CACHE.read() - .unwrap() .guilds .get(&_guild_id) .map(|g| { - g.read().unwrap().members.get(&self.id) + g.read().members.get(&self.id) .map(|m| m.roles.contains(&role_id)) .unwrap_or(false) }) @@ -629,11 +630,12 @@ impl User { /// struct Handler; /// /// impl EventHandler for Handler { - /// fn on_message(&self, _: Context, _: Message) { + /// fn message(&self, _: Context, _: Message) { /// // normal message handling here /// } /// } - /// let mut client = Client::new("token", Handler); + /// + /// let mut client = Client::new("token", Handler).unwrap(); /// # /// use serenity::model::UserId; /// use serenity::CACHE; @@ -651,11 +653,11 @@ impl User { /// loop { /// thread::sleep(duration); /// - /// let cache = CACHE.read().unwrap(); + /// let cache = CACHE.read(); /// /// for id in &special_users { /// if let Some(user) = cache.user(*id) { - /// if let Err(why) = user.write().unwrap().refresh() { + /// if let Err(why) = user.write().refresh() { /// println!("Error refreshing {}: {:?}", id, why); /// } /// } @@ -700,7 +702,7 @@ impl User { /// struct Handler; /// /// impl EventHandler for Handler { - /// fn on_message(&self, _: Context, msg: Message) { + /// fn message(&self, _: Context, msg: Message) { /// if msg.content == "!mytag" { /// let content = MessageBuilder::new() /// .push("Your tag is ") @@ -711,7 +713,9 @@ impl User { /// } /// } /// } - /// let mut client = Client::new("token", Handler); client.start().unwrap(); + /// let mut client = Client::new("token", Handler).unwrap(); + /// + /// client.start().unwrap(); /// ``` #[inline] pub fn tag(&self) -> String { tag(&self.name, self.discriminator) } @@ -741,7 +745,7 @@ impl UserId { /// Search the cache for the user with the Id. #[cfg(feature = "cache")] - pub fn find(&self) -> Option<Arc<RwLock<User>>> { CACHE.read().unwrap().user(*self) } + pub fn find(&self) -> Option<Arc<RwLock<User>>> { CACHE.read().user(*self) } /// Gets a user by its Id over the REST API. /// @@ -750,8 +754,8 @@ impl UserId { pub fn get(&self) -> Result<User> { #[cfg(feature = "cache")] { - if let Some(user) = CACHE.read().unwrap().user(*self) { - return Ok(user.read().unwrap().clone()); + if let Some(user) = CACHE.read().user(*self) { + return Ok(user.read().clone()); } } @@ -771,12 +775,12 @@ impl<'a> From<&'a CurrentUser> for UserId { impl From<Member> for UserId { /// Gets the Id of a `Member`. - fn from(member: Member) -> UserId { member.user.read().unwrap().id } + 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().unwrap().id } + fn from(member: &Member) -> UserId { member.user.read().id } } impl From<User> for UserId { diff --git a/src/model/utils.rs b/src/model/utils.rs index a64156b..d366e5f 100644 --- a/src/model/utils.rs +++ b/src/model/utils.rs @@ -1,6 +1,7 @@ +use parking_lot::RwLock; use serde::de::Error as DeError; use std::collections::HashMap; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; use super::*; #[cfg(feature = "cache")] @@ -44,7 +45,7 @@ pub fn deserialize_members<'de, D: Deserializer<'de>>( let mut members = HashMap::new(); for member in vec { - let user_id = member.user.read().unwrap().id; + let user_id = member.user.read().id; members.insert(user_id, member); } @@ -73,8 +74,8 @@ pub fn deserialize_private_channels<'de, D: Deserializer<'de>>( for private_channel in vec { let id = match private_channel { - Channel::Group(ref group) => group.read().unwrap().channel_id, - Channel::Private(ref channel) => channel.read().unwrap().id, + Channel::Group(ref group) => group.read().channel_id, + Channel::Private(ref channel) => channel.read().id, Channel::Guild(_) => unreachable!("Guild private channel decode"), Channel::Category(_) => unreachable!("Channel category private channel decode"), }; @@ -147,7 +148,7 @@ pub fn deserialize_voice_states<'de, D: Deserializer<'de>>( #[cfg(all(feature = "cache", feature = "model"))] pub fn user_has_perms(channel_id: ChannelId, mut permissions: Permissions) -> Result<bool> { - let cache = CACHE.read().unwrap(); + let cache = CACHE.read(); let current_user = &cache.user; let channel = match cache.channel(channel_id) { @@ -156,7 +157,7 @@ pub fn user_has_perms(channel_id: ChannelId, mut permissions: Permissions) -> Re }; let guild_id = match channel { - Channel::Guild(channel) => channel.read().unwrap().guild_id, + 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 diff --git a/src/model/webhook.rs b/src/model/webhook.rs index c8ae1b0..e00d018 100644 --- a/src/model/webhook.rs +++ b/src/model/webhook.rs @@ -7,7 +7,7 @@ use builder::ExecuteWebhook; #[cfg(feature = "model")] use internal::prelude::*; #[cfg(feature = "model")] -use http; +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 @@ -183,12 +183,9 @@ impl Webhook { wait: bool, f: F) -> Result<Option<Message>> { - http::execute_webhook( - self.id.0, - &self.token, - wait, - &f(ExecuteWebhook::default()).0, - ) + let map = utils::hashmap_to_json_map(f(ExecuteWebhook::default()).0); + + http::execute_webhook(self.id.0, &self.token, wait, &map) } /// Retrieves the latest information about the webhook, editing the |