aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLakelezz <[email protected]>2017-10-10 13:57:07 +0200
committeralex <[email protected]>2017-10-10 13:57:07 +0200
commit002ce3aa272fa51b84e820f12db39cb87a461a83 (patch)
tree267878798fbc3d65ca7af6683f4982cef08d6448 /src
parentReset shard heartbeat state on resume (diff)
downloadserenity-002ce3aa272fa51b84e820f12db39cb87a461a83.tar.xz
serenity-002ce3aa272fa51b84e820f12db39cb87a461a83.zip
Variety of methods to search for `Member`. (#187)
Diffstat (limited to 'src')
-rw-r--r--src/model/guild/mod.rs230
1 files changed, 218 insertions, 12 deletions
diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs
index 383e8f8..711e13c 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;
@@ -747,10 +748,17 @@ impl Guild {
/// - "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|
@@ -767,19 +775,65 @@ impl Guild {
nick.starts_with(prefix)
} else {
starts_with_case_insensitive(&nick, &prefix)
- })).collect()
+ })).collect();
+
+ if sorted {
+ members
+ .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()
+ } else {
+ nick.clone()
+ }
+ },
+ None => a.user.read().unwrap().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()
+ } else {
+ nick.clone()
+ }
+ },
+ None => b.user.read().unwrap().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|
@@ -790,13 +844,144 @@ impl Guild {
}
|| 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().unwrap().name[..], &substring) {
+ a.user.read().unwrap().name.clone()
+ } else {
+ nick.clone()
+ }
+ },
+ None => a.user.read().unwrap().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()
+ } else {
+ nick.clone()
+ }
+ },
+ None => b.user.read().unwrap().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().unwrap().name.contains(substring)
+ } else {
+ contains_case_insensitive(&member.user.read().unwrap().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;
+ 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().unwrap().name.clone(),
+ };
+
+ let name_b = match b.nick {
+ Some(ref nick) => {
+ nick.clone()
+ },
+ None => b.user.read().unwrap().name.clone(),
+ };
+
+ closest_to_origin(&substring, &name_a[..], &name_b[..])
+ });
+ members
+ } else {
+ members
+ }
}
/// Moves a member to a specific voice channel.
@@ -1273,6 +1458,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 {