Skip to content

Commit

Permalink
Allow mentionable structs to be used as command arguments
Browse files Browse the repository at this point in the history
Add EmojiIdentifier, allow User, UserId, Role, RoleId, EmojiIdentifier,
Channel and ChannelId to be used as arguments for commands and add more
parsing functions to utils
  • Loading branch information
fwrs authored and zeyla committed Dec 7, 2016
1 parent 13de5c2 commit 626ffb2
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 1 deletion.
11 changes: 11 additions & 0 deletions definitions/structs/emoji_identifier.yml
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions src/ext/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions src/model/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
82 changes: 81 additions & 1 deletion src/model/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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> {
Expand Down
83 changes: 83 additions & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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`].
Expand Down
33 changes: 33 additions & 0 deletions tests/test_parsers.rs
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 626ffb2

Please sign in to comment.