diff options
Diffstat (limited to 'src/model/guild')
| -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 |
8 files changed, 203 insertions, 152 deletions
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; |