aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--definitions/structs/emoji_identifier.yml11
-rw-r--r--src/ext/cache/mod.rs14
-rw-r--r--src/model/id.rs8
-rw-r--r--src/model/misc.rs82
-rw-r--r--src/utils/mod.rs83
-rw-r--r--tests/test_parsers.rs33
6 files changed, 230 insertions, 1 deletions
diff --git a/definitions/structs/emoji_identifier.yml b/definitions/structs/emoji_identifier.yml
new file mode 100644
index 0000000..01d14dc
--- /dev/null
+++ b/definitions/structs/emoji_identifier.yml
@@ -0,0 +1,11 @@
+---
+name: EmojiIdentifier
+description: Version of emoji struct used only when Id and name are known.
+fields:
+ - name: id
+ description: "The Id of the emoji."
+ type: EmojiId
+ - name: name
+ description: "The name of the emoji. It must be at least 2 characters
+ long and can only contain alphanumeric characters and underscores."
+ type: string
diff --git a/src/ext/cache/mod.rs b/src/ext/cache/mod.rs
index 39baaf0..83ff46f 100644
--- a/src/ext/cache/mod.rs
+++ b/src/ext/cache/mod.rs
@@ -411,6 +411,20 @@ impl Cache {
})
}
+ /// Retrieves a reference to a `User` based on appearance in
+ /// the first server they are in.
+ pub fn get_user<U>(&self, user_id: U) -> Option<&User>
+ where U: Into<UserId> + Clone {
+ for v in self.guilds.values() {
+ match v.members.get(&user_id.clone().into()) {
+ Some(x) => { return Some(&x.user) }
+ None => {}
+ }
+ }
+
+ None
+ }
+
/// Retrieves a reference to a [`Guild`]'s role by their Ids.
///
/// [`Guild`]: ../../model/struct.Guild.html
diff --git a/src/model/id.rs b/src/model/id.rs
index 032e722..cbbd796 100644
--- a/src/model/id.rs
+++ b/src/model/id.rs
@@ -172,6 +172,14 @@ impl RoleId {
}
}
+impl UserId {
+ /// Search the cache for the channel with the Id.
+ #[cfg(all(feature = "cache", feature = "methods"))]
+ pub fn find(&self) -> Option<User> {
+ CACHE.read().unwrap().get_user(*self).map(|x| x.clone())
+ }
+}
+
impl From<CurrentUser> for UserId {
/// Gets the Id of a `CurrentUser` struct.
fn from(current_user: CurrentUser) -> UserId {
diff --git a/src/model/misc.rs b/src/model/misc.rs
index 6e208bf..e078bac 100644
--- a/src/model/misc.rs
+++ b/src/model/misc.rs
@@ -7,9 +7,13 @@ use super::{
Role,
UserId,
User,
- IncidentStatus
+ IncidentStatus,
+ EmojiIdentifier
};
use ::internal::prelude::*;
+use std::str::FromStr;
+use std::result::Result as StdResult;
+use ::utils;
/// Allows something - such as a channel or role - to be mentioned in a message.
pub trait Mentionable {
@@ -74,6 +78,82 @@ impl Mentionable for User {
}
}
+#[cfg(feature = "cache")]
+impl FromStr for User {
+ type Err = ();
+ fn from_str(s: &str) -> StdResult<Self, ()> {
+ match utils::parse_username(s) {
+ Some(x) => {
+ match UserId(x as u64).find() {
+ Some(user) => Ok(user),
+ _ => Err(())
+ }
+ },
+ _ => Err(())
+ }
+ }
+}
+
+impl FromStr for UserId {
+ type Err = ();
+ fn from_str(s: &str) -> StdResult<Self, ()> {
+ utils::parse_username(s).ok_or_else(|| ()).map(|x| UserId(x))
+ }
+}
+
+#[cfg(feature = "cache")]
+impl FromStr for Role {
+ type Err = ();
+ fn from_str(s: &str) -> StdResult<Self, ()> {
+ match utils::parse_role(s) {
+ Some(x) => {
+ match RoleId(x).find() {
+ Some(user) => Ok(user),
+ _ => Err(())
+ }
+ },
+ _ => Err(())
+ }
+ }
+}
+
+impl FromStr for RoleId {
+ type Err = ();
+ fn from_str(s: &str) -> StdResult<Self, ()> {
+ utils::parse_role(s).ok_or_else(|| ()).map(|x| RoleId(x))
+ }
+}
+
+impl FromStr for EmojiIdentifier {
+ type Err = ();
+ fn from_str(s: &str) -> StdResult<Self, ()> {
+ utils::parse_emoji(s).ok_or_else(|| ())
+ }
+}
+
+impl FromStr for ChannelId {
+ type Err = ();
+ fn from_str(s: &str) -> StdResult<Self, ()> {
+ utils::parse_channel(s).ok_or_else(|| ()).map(|x| ChannelId(x))
+ }
+}
+
+#[cfg(feature = "cache")]
+impl FromStr for Channel {
+ type Err = ();
+ fn from_str(s: &str) -> StdResult<Self, ()> {
+ match utils::parse_channel(s) {
+ Some(x) => {
+ match ChannelId(x).find() {
+ Some(channel) => Ok(channel),
+ _ => Err(())
+ }
+ },
+ _ => Err(())
+ }
+ }
+}
+
impl IncidentStatus {
#[doc(hidden)]
pub fn decode(value: Value) -> Result<Self> {
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index aa97f4a..2401a1b 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -18,6 +18,7 @@ use std::fs::File;
use std::io::Read;
use std::path::Path;
use ::internal::prelude::*;
+use ::model::{EmojiIdentifier, EmojiId};
pub use self::message_builder::MessageBuilder;
@@ -83,6 +84,88 @@ pub fn parse_invite(code: &str) -> &str {
}
}
+/// Retreives Id from a username mention.
+pub fn parse_username(mention: &str) -> Option<u64> {
+ if mention.len() < 4 {
+ return None;
+ }
+
+ if mention.starts_with("<@!") {
+ let len = mention.len() - 1;
+ mention[3..len].parse::<u64>().ok()
+ } else if mention.starts_with("<@") {
+ let len = mention.len() - 1;
+ mention[2..len].parse::<u64>().ok()
+ } else {
+ None
+ }
+}
+
+/// Retreives Id from a role mention.
+pub fn parse_role(mention: &str) -> Option<u64> {
+ if mention.len() < 4 {
+ return None;
+ }
+
+ if mention.starts_with("<@&") {
+ let len = mention.len() - 1;
+ mention[3..len].parse::<u64>().ok()
+ } else {
+ None
+ }
+}
+
+/// Retreives Id from a channel mention.
+pub fn parse_channel(mention: &str) -> Option<u64> {
+ if mention.len() < 4 {
+ return None;
+ }
+
+ if mention.starts_with("<#") {
+ let len = mention.len() - 1;
+ mention[2..len].parse::<u64>().ok()
+ } else {
+ None
+ }
+}
+
+/// Retreives name and Id from an emoji mention.
+pub fn parse_emoji(mention: &str) -> Option<EmojiIdentifier> {
+ let len = mention.len();
+ if len < 6 || len > 56 {
+ return None;
+ }
+
+ if mention.starts_with("<:") {
+ let mut name = String::default();
+ let mut id = String::default();
+ for (i, x) in mention[2..].chars().enumerate() {
+ if x == ':' {
+ let from = i + 3;
+ for y in mention[from..].chars() {
+ if y == '>' {
+ break;
+ } else {
+ id.push(y);
+ }
+ }
+ break;
+ } else {
+ name.push(x);
+ }
+ }
+ match id.parse::<u64>() {
+ Ok(x) => Some(EmojiIdentifier {
+ name: name,
+ id: EmojiId(x)
+ }),
+ _ => None
+ }
+ } else {
+ None
+ }
+}
+
/// Reads an image from a path and encodes it into base64.
///
/// This can be used for methods like [`EditProfile::avatar`].
diff --git a/tests/test_parsers.rs b/tests/test_parsers.rs
new file mode 100644
index 0000000..479ac93
--- /dev/null
+++ b/tests/test_parsers.rs
@@ -0,0 +1,33 @@
+extern crate serenity;
+
+use serenity::utils::*;
+
+#[test]
+fn invite_parser() {
+ assert_eq!(parse_invite("https://discord.gg/abc"), "abc");
+ assert_eq!(parse_invite("http://discord.gg/abc"), "abc");
+ assert_eq!(parse_invite("discord.gg/abc"), "abc");
+}
+
+#[test]
+fn username_parser() {
+ assert_eq!(parse_username("<@12345>").unwrap(), 12345);
+ assert_eq!(parse_username("<@!12345>").unwrap(), 12345);
+}
+
+#[test]
+fn role_parser() {
+ assert_eq!(parse_role("<@&12345>").unwrap(), 12345);
+}
+
+#[test]
+fn channel_parser() {
+ assert_eq!(parse_channel("<#12345>").unwrap(), 12345);
+}
+
+#[test]
+fn emoji_parser() {
+ let emoji = parse_emoji("<:name:12345>").unwrap();
+ assert_eq!(emoji.name, "name");
+ assert_eq!(emoji.id, 12345);
+}