Skip to content

Commit

Permalink
Variety of methods to search for Member. (#187)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lakelezz authored and arqunis committed Oct 10, 2017
1 parent c98cae4 commit 002ce3a
Showing 1 changed file with 218 additions and 12 deletions.
230 changes: 218 additions & 12 deletions src/model/guild/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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|

Expand All @@ -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|

Expand All @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 002ce3a

Please sign in to comment.