diff options
| -rw-r--r-- | definitions/structs/emoji_identifier.yml | 11 | ||||
| -rw-r--r-- | src/ext/cache/mod.rs | 14 | ||||
| -rw-r--r-- | src/model/id.rs | 8 | ||||
| -rw-r--r-- | src/model/misc.rs | 82 | ||||
| -rw-r--r-- | src/utils/mod.rs | 83 | ||||
| -rw-r--r-- | tests/test_parsers.rs | 33 |
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); +} |