aboutsummaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
authoracdenisSK <[email protected]>2017-10-14 22:41:25 +0200
committeracdenisSK <[email protected]>2017-10-14 22:41:25 +0200
commitcae014758a1d1e926a71679f02e32601c57f8d52 (patch)
tree39270dbc2df916a91c3c4272600fd082a2604516 /src/model
parentSwitch to parking_lot::{Mutex, RwLock} (diff)
parentRelease v0.4.1 (diff)
downloadserenity-cae014758a1d1e926a71679f02e32601c57f8d52.tar.xz
serenity-cae014758a1d1e926a71679f02e32601c57f8d52.zip
Update to account for changes made in 0.4.1
Diffstat (limited to 'src/model')
-rw-r--r--src/model/channel/message.rs4
-rw-r--r--src/model/event.rs34
-rw-r--r--src/model/guild/member.rs34
-rw-r--r--src/model/guild/mod.rs239
-rw-r--r--src/model/permissions.rs2
5 files changed, 251 insertions, 62 deletions
diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs
index f6efd74..0e939b2 100644
--- a/src/model/channel/message.rs
+++ b/src/model/channel/message.rs
@@ -370,7 +370,7 @@ impl Message {
// Check if the content is over the maximum number of unicode code
// points.
let count = content.chars().count() as i64;
- let diff = count - (constants::MESSAGE_CODE_LIMIT as i64);
+ let diff = count - i64::from(constants::MESSAGE_CODE_LIMIT);
if diff > 0 {
Some(diff as u64)
@@ -561,7 +561,7 @@ impl Message {
if total <= constants::EMBED_MAX_LENGTH as usize {
Ok(())
} else {
- let overflow = total as u64 - constants::EMBED_MAX_LENGTH as u64;
+ let overflow = total as u64 - u64::from(constants::EMBED_MAX_LENGTH);
Err(Error::Model(ModelError::EmbedTooLarge(overflow)))
}
diff --git a/src/model/event.rs b/src/model/event.rs
index 8d949c7..90a6828 100644
--- a/src/model/event.rs
+++ b/src/model/event.rs
@@ -57,13 +57,13 @@ impl CacheUpdate for ChannelCreateEvent {
fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> {
match self.channel {
Channel::Group(ref group) => {
- let group = group.clone();
+ let group = Arc::clone(group);
let channel_id = group.with_mut(|writer| {
for (recipient_id, recipient) in &mut writer.recipients {
cache.update_user_entry(&recipient.read());
- *recipient = cache.users[recipient_id].clone();
+ *recipient = Arc::clone(&cache.users[recipient_id]);
}
writer.channel_id
@@ -76,23 +76,23 @@ impl CacheUpdate for ChannelCreateEvent {
Channel::Guild(ref channel) => {
let (guild_id, channel_id) = channel.with(|channel| (channel.guild_id, channel.id));
- cache.channels.insert(channel_id, channel.clone());
+ cache.channels.insert(channel_id, Arc::clone(channel));
cache
.guilds
.get_mut(&guild_id)
.and_then(|guild| {
guild
- .with_mut(|guild| guild.channels.insert(channel_id, channel.clone()))
+ .with_mut(|guild| guild.channels.insert(channel_id, Arc::clone(channel)))
})
.map(Channel::Guild)
},
Channel::Private(ref channel) => {
if let Some(channel) = cache.private_channels.get(&channel.with(|c| c.id)) {
- return Some(Channel::Private((*channel).clone()));
+ return Some(Channel::Private(Arc::clone(&(*channel))));
}
- let channel = channel.clone();
+ let channel = Arc::clone(channel);
let id = channel.with_mut(|writer| {
let user_id = writer.recipient.with_mut(|user| {
@@ -101,16 +101,16 @@ impl CacheUpdate for ChannelCreateEvent {
user.id
});
- writer.recipient = cache.users[&user_id].clone();
+ writer.recipient = Arc::clone(&cache.users[&user_id]);
writer.id
});
- let ch = cache.private_channels.insert(id, channel.clone());
+ let ch = cache.private_channels.insert(id, Arc::clone(&channel));
ch.map(Channel::Private)
},
Channel::Category(ref category) => cache
.categories
- .insert(category.read().id, category.clone())
+ .insert(category.read().id, Arc::clone(category))
.map(Channel::Category),
}
}
@@ -211,7 +211,7 @@ impl CacheUpdate for ChannelRecipientAddEvent {
fn update(&mut self, cache: &mut Cache) -> Option<()> {
cache.update_user_entry(&self.user);
- let user = cache.users[&self.user.id].clone();
+ let user = Arc::clone(&cache.users[&self.user.id]);
cache.groups.get_mut(&self.channel_id).map(|group| {
group.write().recipients.insert(self.user.id, user);
@@ -260,7 +260,7 @@ impl CacheUpdate for ChannelUpdateEvent {
match cache.groups.entry(ch_id) {
Entry::Vacant(e) => {
- e.insert(group.clone());
+ e.insert(Arc::clone(group));
},
Entry::Occupied(mut e) => {
let mut dest = e.get_mut().write();
@@ -280,10 +280,10 @@ impl CacheUpdate for ChannelUpdateEvent {
Channel::Guild(ref channel) => {
let (guild_id, channel_id) = channel.with(|channel| (channel.guild_id, channel.id));
- cache.channels.insert(channel_id, channel.clone());
+ cache.channels.insert(channel_id, Arc::clone(channel));
cache.guilds.get_mut(&guild_id).map(|guild| {
guild
- .with_mut(|g| g.channels.insert(channel_id, channel.clone()))
+ .with_mut(|g| g.channels.insert(channel_id, Arc::clone(channel)))
});
},
Channel::Private(ref channel) => {
@@ -341,9 +341,9 @@ impl CacheUpdate for GuildCreateEvent {
for (user_id, member) in &mut guild.members {
cache.update_user_entry(&member.user.read());
- let user = cache.users[user_id].clone();
+ let user = Arc::clone(&cache.users[user_id]);
- member.user = user.clone();
+ member.user = Arc::clone(&user);
}
cache.channels.extend(guild.channels.clone());
@@ -431,7 +431,7 @@ impl CacheUpdate for GuildMemberAddEvent {
cache.update_user_entry(&self.member.user.read());
// Always safe due to being inserted above.
- self.member.user = cache.users[&user_id].clone();
+ self.member.user = Arc::clone(&cache.users[&user_id]);
cache.guilds.get_mut(&self.guild_id).map(|guild| {
guild.with_mut(|guild| {
@@ -766,7 +766,7 @@ impl CacheUpdate for PresenceUpdateEvent {
if let Some(user) = self.presence.user.as_mut() {
cache.update_user_entry(&user.read());
- *user = cache.users[&user_id].clone();
+ *user = Arc::clone(&cache.users[&user_id]);
}
if let Some(guild_id) = self.guild_id {
diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs
index bca3ecb..02e5afb 100644
--- a/src/model/guild/member.rs
+++ b/src/model/guild/member.rs
@@ -159,10 +159,7 @@ impl Member {
#[cfg(all(feature = "cache", feature = "utils"))]
pub fn colour(&self) -> Option<Colour> {
let cache = CACHE.read();
- let guild = match cache.guilds.get(&self.guild_id) {
- Some(guild) => guild.read(),
- None => return None,
- };
+ let guild = try_opt!(cache.guilds.get(&self.guild_id)).read();
let mut roles = self.roles
.iter()
@@ -382,25 +379,16 @@ impl Member {
/// If role data can not be found for the member, then `None` is returned.
#[cfg(feature = "cache")]
pub fn roles(&self) -> Option<Vec<Role>> {
- CACHE
- .read()
- .guilds
- .values()
- .find(|guild| {
- guild.read().members.values().any(|m| {
- m.user.read().id == self.user.read().id &&
- m.joined_at == self.joined_at
- })
- })
- .map(|guild| {
- guild
- .read()
- .roles
- .values()
- .filter(|role| self.roles.contains(&role.id))
- .cloned()
- .collect()
- })
+ self
+ .guild_id
+ .find()
+ .map(|g| g
+ .read()
+ .roles
+ .values()
+ .filter(|role| self.roles.contains(&role.id))
+ .cloned()
+ .collect())
}
/// Unbans the [`User`] from the guild.
diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs
index ea6cb17..110ef4e 100644
--- a/src/model/guild/mod.rs
+++ b/src/model/guild/mod.rs
@@ -21,6 +21,7 @@ use serde::de::Error as DeError;
use serde_json;
use super::utils::*;
use model::*;
+use std;
#[cfg(all(feature = "cache", feature = "model"))]
use CACHE;
@@ -740,21 +741,23 @@ 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:
+ /// - "zeya", "zeyaa", "zeyla", "zeyzey", "zeyzeyzey"
+ /// It would be sorted:
+ /// - "zeya", "zeyaa", "zeyla", "zeyzey", "zeyzeyzey"
///
/// [`Member`]: struct.Member.html
- pub fn members_starting_with(&self, prefix: &str, case_sensitive: bool) -> Vec<&Member> {
- self.members
+ pub fn members_starting_with(&self, prefix: &str, case_sensitive: bool, sorted: bool) -> Vec<&Member> {
+ let mut members: Vec<&Member> = self.members
.values()
.filter(|member|
if case_sensitive {
member.user.read().name.starts_with(prefix)
} else {
- starts_with_case_insensitive(&member.user.read().name, &prefix)
+ starts_with_case_insensitive(&member.user.read().name, prefix)
}
|| member.nick.as_ref()
@@ -763,37 +766,214 @@ impl Guild {
if case_sensitive {
nick.starts_with(prefix)
} else {
- starts_with_case_insensitive(&nick, &prefix)
- })).collect()
+ starts_with_case_insensitive(nick, prefix)
+ })).collect();
+
+ if sorted {
+ members
+ .sort_by(|a, b| {
+ let name_a = match a.nick {
+ Some(ref nick) => {
+ if contains_case_insensitive(&a.user.read().name[..], prefix) {
+ a.user.read().name.clone()
+ } else {
+ nick.clone()
+ }
+ },
+ None => a.user.read().name.clone(),
+ };
+
+ let name_b = match b.nick {
+ Some(ref nick) => {
+ if contains_case_insensitive(&b.user.read().name[..], prefix) {
+ b.user.read().name.clone()
+ } else {
+ nick.clone()
+ }
+ },
+ None => b.user.read().name.clone(),
+ };
+
+ closest_to_origin(prefix, &name_a[..], &name_b[..])
+ });
+ members
+ } else {
+ members
+ }
}
- /// Retrieves all [`Member`] containing a given `String`.
+ /// Retrieves all [`Member`] containing a given `String` as
+ /// either username or nick, with a priority on username.
///
/// If the substring is "yla", following results are possible:
/// - "zeyla", "meiyla", "yladenisyla"
/// If 'case_sensitive' is false, the following are not found:
/// - "zeYLa", "meiyLa", "LYAdenislyA"
///
+ /// `sorted` decides whether the best early match of the search-term
+ /// should be the criteria to sort the result.
+ /// It will look at the account name first, if that does not fit the
+ /// search-criteria `substring`, the display-name will be considered.
+ /// For the `substring` "zey" and the unsorted result:
+ /// - "azey", "zey", "zeyla", "zeylaa", "zeyzeyzey"
+ /// It would be sorted:
+ /// - "zey", "azey", "zeyla", "zeylaa", "zeyzeyzey"
+ ///
+ /// **Note**: Due to two fields of a `Member` being candidates for
+ /// the searched field, setting `sorted` to `true` will result in an overhead,
+ /// as both fields have to be considered again for sorting.
+ ///
/// [`Member`]: struct.Member.html
- pub fn members_containing(&self, substring: &str, case_sensitive: bool) -> Vec<&Member> {
- self.members
+ pub fn members_containing(&self, substring: &str, case_sensitive: bool, sorted: bool) -> Vec<&Member> {
+ let mut members: Vec<&Member> = self.members
.values()
.filter(|member|
if case_sensitive {
member.user.read().name.contains(substring)
} else {
- contains_case_insensitive(&member.user.read().name, &substring)
+ contains_case_insensitive(&member.user.read().name, substring)
}
|| member.nick.as_ref()
- .map_or(false, |nick|
+ .map_or(false, |nick| {
+
+ if case_sensitive {
+ nick.contains(substring)
+ } else {
+ contains_case_insensitive(nick, substring)
+ }
+ })).collect();
+
+ if sorted {
+ members
+ .sort_by(|a, b| {
+ let name_a = match a.nick {
+ Some(ref nick) => {
+ if contains_case_insensitive(&a.user.read().name[..], substring) {
+ a.user.read().name.clone()
+ } else {
+ nick.clone()
+ }
+ },
+ None => a.user.read().name.clone(),
+ };
+
+ let name_b = match b.nick {
+ Some(ref nick) => {
+ if contains_case_insensitive(&b.user.read().name[..], substring) {
+ b.user.read().name.clone()
+ } else {
+ nick.clone()
+ }
+ },
+ None => b.user.read().name.clone(),
+ };
+
+ closest_to_origin(substring, &name_a[..], &name_b[..])
+ });
+ members
+ } else {
+ members
+ }
+ }
- if case_sensitive {
- nick.starts_with(substring)
- } else {
- contains_case_insensitive(&nick, &substring)
- })).collect()
+ /// Retrieves all [`Member`] containing a given `String` in
+ /// their username.
+ ///
+ /// If the substring is "yla", following results are possible:
+ /// - "zeyla", "meiyla", "yladenisyla"
+ /// If 'case_sensitive' is false, the following are not found:
+ /// - "zeYLa", "meiyLa", "LYAdenislyA"
+ ///
+ /// `sort` decides whether the best early match of the search-term
+ /// should be the criteria to sort the result.
+ /// For the `substring` "zey" and the unsorted result:
+ /// - "azey", "zey", "zeyla", "zeylaa", "zeyzeyzey"
+ /// It would be sorted:
+ /// - "zey", "azey", "zeyla", "zeylaa", "zeyzeyzey"
+ ///
+ /// [`Member`]: struct.Member.html
+ pub fn members_username_containing(&self, substring: &str, case_sensitive: bool, sorted: bool) -> Vec<&Member> {
+ let mut members: Vec<&Member> = self.members
+ .values()
+ .filter(|member| {
+ if case_sensitive {
+ member.user.read().name.contains(substring)
+ } else {
+ contains_case_insensitive(&member.user.read().name, substring)
+ }
+ }).collect();
+
+ if sorted {
+ members
+ .sort_by(|a, b| {
+ let name_a = &a.user.read().name;
+ let name_b = &b.user.read().name;
+ closest_to_origin(substring, &name_a[..], &name_b[..])
+ });
+ members
+ } else {
+ members
+ }
+ }
+
+ /// Retrieves all [`Member`] containing a given `String` in
+ /// their nick.
+ ///
+ /// If the substring is "yla", following results are possible:
+ /// - "zeyla", "meiyla", "yladenisyla"
+ /// If 'case_sensitive' is false, the following are not found:
+ /// - "zeYLa", "meiyLa", "LYAdenislyA"
+ ///
+ /// `sort` decides whether the best early match of the search-term
+ /// should be the criteria to sort the result.
+ /// For the `substring` "zey" and the unsorted result:
+ /// - "azey", "zey", "zeyla", "zeylaa", "zeyzeyzey"
+ /// It would be sorted:
+ /// - "zey", "azey", "zeyla", "zeylaa", "zeyzeyzey"
+ ///
+ /// **Note**: Instead of panicing, when sorting does not find
+ /// a nick, the username will be used (this should never happen).
+ ///
+ /// [`Member`]: struct.Member.html
+ pub fn members_nick_containing(&self, substring: &str, case_sensitive: bool, sorted: bool) -> Vec<&Member> {
+ let mut members: Vec<&Member> = self.members
+ .values()
+ .filter(|member|
+ member.nick.as_ref()
+ .map_or(false, |nick| {
+
+ if case_sensitive {
+ nick.contains(substring)
+ } else {
+ contains_case_insensitive(nick, substring)
+ }
+ })).collect();
+
+ if sorted {
+ members
+ .sort_by(|a, b| {
+ let name_a = match a.nick {
+ Some(ref nick) => {
+ nick.clone()
+ },
+ None => a.user.read().name.clone(),
+ };
+
+ let name_b = match b.nick {
+ Some(ref nick) => {
+ nick.clone()
+ },
+ None => b.user.read().name.clone(),
+ };
+
+ closest_to_origin(substring, &name_a[..], &name_b[..])
+ });
+ members
+ } else {
+ members
+ }
}
/// Moves a member to a specific voice channel.
@@ -1270,6 +1450,27 @@ fn starts_with_case_insensitive(to_look_at: &str, to_find: &str) -> bool {
to_look_at.to_lowercase().starts_with(to_find)
}
+/// Takes a `&str` as `origin` and tests if either
+/// `word_a` or `word_b` is closer.
+///
+/// **Note**: Normally `word_a` and `word_b` are
+/// expected to contain `origin` as substring.
+/// If not, using `closest_to_origin` would sort these
+/// the end.
+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(),
+ None => return std::cmp::Ordering::Greater,
+ };
+
+ let value_b = match word_b.find(origin) {
+ Some(value) => value + word_b.len(),
+ None => return std::cmp::Ordering::Less,
+ };
+
+ value_a.cmp(&value_b)
+}
+
/// Information relating to a guild's widget embed.
#[derive(Clone, Copy, Debug, Deserialize)]
pub struct GuildEmbed {
diff --git a/src/model/permissions.rs b/src/model/permissions.rs
index 22599ea..51aafe5 100644
--- a/src/model/permissions.rs
+++ b/src/model/permissions.rs
@@ -445,7 +445,7 @@ impl<'de> Visitor<'de> for U64Visitor {
fn visit_i64<E: DeError>(self, value: i64) -> StdResult<u64, E> { Ok(value as u64) }
- fn visit_u32<E: DeError>(self, value: u32) -> StdResult<u64, E> { Ok(value as u64) }
+ fn visit_u32<E: DeError>(self, value: u32) -> StdResult<u64, E> { Ok(u64::from(value)) }
fn visit_u64<E: DeError>(self, value: u64) -> StdResult<u64, E> { Ok(value) }
}