diff options
| author | Lakelezz <[email protected]> | 2017-10-10 13:57:07 +0200 |
|---|---|---|
| committer | alex <[email protected]> | 2017-10-10 13:57:07 +0200 |
| commit | 002ce3aa272fa51b84e820f12db39cb87a461a83 (patch) | |
| tree | 267878798fbc3d65ca7af6683f4982cef08d6448 /src | |
| parent | Reset shard heartbeat state on resume (diff) | |
| download | serenity-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.rs | 230 |
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 { |