mod colour;
mod message_builder;
mod vec_map;
pub use self::{
colour::Colour,
message_builder::{Content, ContentModifier, MessageBuilder},
vec_map::VecMap
};
use base64;
use internal::prelude::*;
use model::id::EmojiId;
use model::misc::EmojiIdentifier;
use std::{
collections::HashMap,
ffi::OsStr,
fs::File,
hash::{BuildHasher, Hash},
io::Read,
path::Path
};
#[cfg(feature = "cache")]
use cache::Cache;
#[cfg(feature = "cache")]
use CACHE;
pub fn hashmap_to_json_map<H, T>(map: HashMap<T, Value, H>)
-> Map<String, Value> where H: BuildHasher, T: Eq + Hash + ToString {
let mut json_map = Map::new();
for (key, value) in map {
json_map.insert(key.to_string(), value);
}
json_map
}
pub fn vecmap_to_json_map<K: PartialEq + ToString>(map: VecMap<K, Value>) -> Map<String, Value> {
let mut json_map = Map::new();
for (key, value) in map {
json_map.insert(key.to_string(), value);
}
json_map
}
pub fn is_nsfw(name: &str) -> bool {
name == "nsfw" || name.chars().count() > 5 && name.starts_with("nsfw-")
}
pub fn parse_invite(code: &str) -> &str {
if code.starts_with("https://discord.gg/") {
&code[19..]
} else if code.starts_with("http://discord.gg/") {
&code[18..]
} else if code.starts_with("discord.gg/") {
&code[11..]
} else {
code
}
}
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
}
}
pub fn parse_role(mention: &str) -> Option<u64> {
if mention.len() < 4 {
return None;
}
if mention.starts_with("<@&") && mention.ends_with('>') {
let len = mention.len() - 1;
mention[3..len].parse::<u64>().ok()
} else {
None
}
}
pub fn parse_channel(mention: &str) -> Option<u64> {
if mention.len() < 4 {
return None;
}
if mention.starts_with("<#") && mention.ends_with('>') {
let len = mention.len() - 1;
mention[2..len].parse::<u64>().ok()
} else {
None
}
}
pub fn parse_emoji(mention: &str) -> Option<EmojiIdentifier> {
let len = mention.len();
if len < 6 || len > 56 {
return None;
}
if mention.starts_with("<:") && mention.ends_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,
id: EmojiId(x),
}),
_ => None,
}
} else {
None
}
}
#[inline]
pub fn read_image<P: AsRef<Path>>(path: P) -> Result<String> {
_read_image(path.as_ref())
}
fn _read_image(path: &Path) -> Result<String> {
let mut v = Vec::default();
let mut f = File::open(path)?;
let _ = f.read_to_end(&mut v);
let b64 = base64::encode(&v);
let ext = if path.extension() == Some(OsStr::new("png")) {
"png"
} else {
"jpg"
};
Ok(format!("data:image/{};base64,{}", ext, b64))
}
pub fn parse_quotes(s: &str) -> Vec<String> {
let mut args = vec![];
let mut in_string = false;
let mut escaping = false;
let mut current_str = String::default();
for x in s.chars() {
if in_string {
if x == '\\' && !escaping {
escaping = true;
} else if x == '"' && !escaping {
if !current_str.is_empty() {
args.push(current_str);
}
current_str = String::default();
in_string = false;
} else {
current_str.push(x);
escaping = false;
}
} else if x == ' ' {
if !current_str.is_empty() {
args.push(current_str.clone());
}
current_str = String::default();
} else if x == '"' {
if !current_str.is_empty() {
args.push(current_str.clone());
}
in_string = true;
current_str = String::default();
} else {
current_str.push(x);
}
}
if !current_str.is_empty() {
args.push(current_str);
}
args
}
#[inline]
pub fn shard_id(guild_id: u64, shard_count: u64) -> u64 { (guild_id >> 22) % shard_count }
#[cfg(feature = "cache")]
pub fn with_cache<T, F>(f: F) -> T
where F: Fn(&Cache) -> T {
let cache = CACHE.read();
f(&cache)
}
#[cfg(feature = "cache")]
pub fn with_cache_mut<T, F>(mut f: F) -> T
where F: FnMut(&mut Cache) -> T {
let mut cache = CACHE.write();
f(&mut cache)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_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 test_username_parser() {
assert_eq!(parse_username("<@12345>").unwrap(), 12_345);
assert_eq!(parse_username("<@!12345>").unwrap(), 12_345);
}
#[test]
fn role_parser() {
assert_eq!(parse_role("<@&12345>").unwrap(), 12_345);
}
#[test]
fn test_channel_parser() {
assert_eq!(parse_channel("<#12345>").unwrap(), 12_345);
}
#[test]
fn test_emoji_parser() {
let emoji = parse_emoji("<:name:12345>").unwrap();
assert_eq!(emoji.name, "name");
assert_eq!(emoji.id, 12_345);
}
#[test]
fn test_quote_parser() {
let parsed = parse_quotes("a \"b c\" d\"e f\" g");
assert_eq!(parsed, ["a", "b c", "d", "e f", "g"]);
}
#[test]
fn test_is_nsfw() {
assert!(!is_nsfw("general"));
assert!(is_nsfw("nsfw"));
assert!(is_nsfw("nsfw-test"));
assert!(!is_nsfw("nsfw-"));
assert!(!is_nsfw("général"));
assert!(is_nsfw("nsfw-général"));
}
}