diff options
43 files changed, 389 insertions, 670 deletions
@@ -296,7 +296,7 @@ impl {0} {{ pub fn decode(value: Value) -> Result<{0}> {{ let mut map = try!(into_map(value)); - missing!(map, {0} {{", name); + Ok({0} {{", name); let decoding = doc["decode"].as_bool().unwrap_or(true); diff --git a/definitions/structs/bot_application.yml b/definitions/structs/bot_application.yml index 2eb7a66..a1da049 100644 --- a/definitions/structs/bot_application.yml +++ b/definitions/structs/bot_application.yml @@ -15,7 +15,7 @@ fields: description: "Whether or not this is a bot." type: bool - name: discriminator - custom: parse_discriminator + custom: decode_discriminator description: "The discriminator assigned to the user's username. While discriminators are not unique, the username#discriminator combination is." type: u16 diff --git a/definitions/structs/current_user.yml b/definitions/structs/current_user.yml index 1d9c3f2..948a828 100644 --- a/definitions/structs/current_user.yml +++ b/definitions/structs/current_user.yml @@ -11,7 +11,7 @@ fields: default: 'false' type: bool - name: discriminator - custom: parse_discriminator + custom: decode_discriminator type: u16 - name: email optional: true diff --git a/examples/01_basic_ping_bot/Cargo.toml b/examples/01_basic_ping_bot/Cargo.toml new file mode 100644 index 0000000..603ee6b --- /dev/null +++ b/examples/01_basic_ping_bot/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "01_basic_ping_bot" +version = "0.1.0" +authors = ["my name <[email protected]>"] + +[dependencies] +serenity = { git = "https://github.com/zeyla/serenity.rs.git" } diff --git a/examples/01_basic_ping_bot.rs b/examples/01_basic_ping_bot/src/main.rs index 8db4614..8db4614 100644 --- a/examples/01_basic_ping_bot.rs +++ b/examples/01_basic_ping_bot/src/main.rs diff --git a/examples/02_transparent_guild_sharding/Cargo.toml b/examples/02_transparent_guild_sharding/Cargo.toml new file mode 100644 index 0000000..3899f6d --- /dev/null +++ b/examples/02_transparent_guild_sharding/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "02_transparent_guild_sharding" +version = "0.1.0" +authors = ["my name <[email protected]>"] + +[dependencies] +serenity = { git = "https://github.com/zeyla/serenity.rs.git" } diff --git a/examples/02_transparent_guild_sharding.rs b/examples/02_transparent_guild_sharding/src/main.rs index 19b61d0..19b61d0 100644 --- a/examples/02_transparent_guild_sharding.rs +++ b/examples/02_transparent_guild_sharding/src/main.rs diff --git a/examples/03_struct_utilities/Cargo.toml b/examples/03_struct_utilities/Cargo.toml new file mode 100644 index 0000000..8dc013c --- /dev/null +++ b/examples/03_struct_utilities/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "03_struct_utilities" +version = "0.1.0" +authors = ["my name <[email protected]>"] + +[dependencies] +serenity = { git = "https://github.com/zeyla/serenity.rs.git" } diff --git a/examples/03_struct_utilities.rs b/examples/03_struct_utilities/src/main.rs index ea91500..6bf436d 100644 --- a/examples/03_struct_utilities.rs +++ b/examples/03_struct_utilities/src/main.rs @@ -1,13 +1,18 @@ -//! Requires the 'methods' feature flag be enabled. +//! Requires the 'methods' feature flag be enabled in your project's Cargo.toml. +//! +//! This can be activated by specifying the feature in the dependency section: +//! +//! ```toml +//! [dependencies.serenity] +//! git = "https://github.com/zeyla/serenity.rs.git" +//! features = ["methods"] +//! ``` extern crate serenity; -#[cfg(feature = "methods")] use serenity::Client; -#[cfg(feature = "methods")] use std::env; -#[cfg(feature = "methods")] fn main() { // Configure the client with your Discord bot token in the environment. let token = env::var("DISCORD_TOKEN") @@ -26,8 +31,3 @@ fn main() { let _ = client.start(); } - -#[cfg(not(feature = "methods"))] -fn main() { - println!("The 'methods' feature flag is required for this example."); -} diff --git a/examples/04_message_builder/Cargo.toml b/examples/04_message_builder/Cargo.toml new file mode 100644 index 0000000..cf0108f --- /dev/null +++ b/examples/04_message_builder/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "04_message_builder" +version = "0.1.0" +authors = ["my name <[email protected]>"] + +[dependencies] +serenity = { git = "https://github.com/zeyla/serenity.rs.git" } diff --git a/examples/04_message_builder.rs b/examples/04_message_builder/src/main.rs index 41f390b..41f390b 100644 --- a/examples/04_message_builder.rs +++ b/examples/04_message_builder/src/main.rs diff --git a/examples/05_user_login/Cargo.toml b/examples/05_user_login/Cargo.toml new file mode 100644 index 0000000..0f0a19e --- /dev/null +++ b/examples/05_user_login/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "05_user_login" +version = "0.1.0" +authors = ["my name <[email protected]>"] + +[dependencies] +serenity = { git = "https://github.com/zeyla/serenity.rs.git" } diff --git a/examples/05_user_login.rs b/examples/05_user_login/src/main.rs index bbc9303..bbc9303 100644 --- a/examples/05_user_login.rs +++ b/examples/05_user_login/src/main.rs diff --git a/examples/06_command_framework/Cargo.toml b/examples/06_command_framework/Cargo.toml new file mode 100644 index 0000000..fa93475 --- /dev/null +++ b/examples/06_command_framework/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "06_command_framework" +version = "0.1.0" +authors = ["my name <[email protected]>"] + +[dependencies] +serenity = { git = "https://github.com/zeyla/serenity.rs.git" } diff --git a/examples/06_command_framework.rs b/examples/06_command_framework/src/main.rs index 78e4e9e..95d832c 100644 --- a/examples/06_command_framework.rs +++ b/examples/06_command_framework/src/main.rs @@ -1,3 +1,13 @@ +//! Requires the 'methods' feature flag be enabled in your project's Cargo.toml. +//! +//! This can be activated by specifying the feature in the dependency section: +//! +//! ```toml +//! [dependencies.serenity] +//! git = "https://github.com/zeyla/serenity.rs.git" +//! features = ["framework", methods"] +//! ``` + #[macro_use] extern crate serenity; @@ -56,18 +66,10 @@ fn dog_command(context: &Context, _msg: &Message, _args: Vec<String>) { } // `Message::reply` is only compiled if the `methods` feature flag is enabled. -#[cfg(feature = "methods")] fn ping_command(_context: &Context, message: &Message, _args: Vec<String>) { let _ = message.reply("Pong!"); } -#[cfg(not(feature = "methods"))] -fn ping_command(context: &Context, message: &Message, _args: Vec<String>) { - if let Err(why) = context.say(&format!("{}: Pong!", message.author)) { - println!("Error sending message: {:?}", why); - } -} - fn owner_check(_context: &Context, message: &Message) -> bool { // Replace 7 with your ID message.author.id == 7 diff --git a/examples/07_voice/Cargo.toml b/examples/07_voice/Cargo.toml new file mode 100644 index 0000000..d17a835 --- /dev/null +++ b/examples/07_voice/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "07_voice" +version = "0.1.0" +authors = ["my name <[email protected]>"] + +[dependencies] +serenity = { git = "https://github.com/zeyla/serenity.rs.git" } diff --git a/examples/07_voice.rs b/examples/07_voice/src/main.rs index e685f37..fe02245 100644 --- a/examples/07_voice.rs +++ b/examples/07_voice/src/main.rs @@ -1,24 +1,20 @@ -// Requires the feature "voice" be enabled. +//! Requires the "cache", "methods", and "voice" features be enabled in your +//! Cargo.toml, like so: +//! +//! ```toml +//! [dependencies.serenity] +//! version = "*" +//! features = ["cache", "methods", "voice"] +//! ``` extern crate serenity; -#[cfg(feature = "voice")] use serenity::client::{CACHE, Client, Context}; -#[cfg(feature = "voice")] use serenity::ext::voice; -#[cfg(feature = "voice")] use serenity::model::{ChannelId, Message}; -#[cfg(feature = "voice")] use serenity::Result as SerenityResult; -#[cfg(feature = "voice")] use std::env; -#[cfg(not(feature = "voice"))] -fn main() { - panic!("'extras' and 'voice' must be enabled"); -} - -#[cfg(feature = "voice")] fn main() { // Configure the client with your Discord bot token in the environment. let token = env::var("DISCORD_TOKEN") @@ -45,7 +41,6 @@ fn main() { let _ = client.start().map_err(|why| println!("Client ended: {:?}", why)); } -#[cfg(feature = "voice")] fn deafen(context: &Context, message: &Message, _args: Vec<String>) { let guild_id = match CACHE.read().unwrap().get_guild_channel(message.channel_id) { Some(channel) => channel.guild_id, @@ -76,7 +71,6 @@ fn deafen(context: &Context, message: &Message, _args: Vec<String>) { } } -#[cfg(feature = "voice")] fn join(context: &Context, message: &Message, args: Vec<String>) { let connect_to = match args.get(0) { Some(arg) => match arg.parse::<u64>() { @@ -109,7 +103,6 @@ fn join(context: &Context, message: &Message, args: Vec<String>) { check_msg(context.say(&format!("Joined {}", connect_to.mention()))); } -#[cfg(feature = "voice")] fn leave(context: &Context, message: &Message, _args: Vec<String>) { let guild_id = match CACHE.read().unwrap().get_guild_channel(message.channel_id) { Some(channel) => channel.guild_id, @@ -132,7 +125,6 @@ fn leave(context: &Context, message: &Message, _args: Vec<String>) { } } -#[cfg(feature = "voice")] fn mute(context: &Context, message: &Message, _args: Vec<String>) { let guild_id = match CACHE.read().unwrap().get_guild_channel(message.channel_id) { Some(channel) => channel.guild_id, @@ -163,12 +155,10 @@ fn mute(context: &Context, message: &Message, _args: Vec<String>) { } } -#[cfg(feature = "voice")] fn ping(context: &Context, _message: &Message, _args: Vec<String>) { check_msg(context.say("Pong!")); } -#[cfg(feature = "voice")] fn play(context: &Context, message: &Message, args: Vec<String>) { let url = match args.get(0) { Some(url) => url, @@ -214,7 +204,6 @@ fn play(context: &Context, message: &Message, args: Vec<String>) { } } -#[cfg(feature = "voice")] fn undeafen(context: &Context, message: &Message, _args: Vec<String>) { let guild_id = match CACHE.read().unwrap().get_guild_channel(message.channel_id) { Some(channel) => channel.guild_id, @@ -234,7 +223,6 @@ fn undeafen(context: &Context, message: &Message, _args: Vec<String>) { } } -#[cfg(feature = "voice")] fn unmute(context: &Context, message: &Message, _args: Vec<String>) { let guild_id = match CACHE.read().unwrap().get_guild_channel(message.channel_id) { Some(channel) => channel.guild_id, @@ -255,6 +243,7 @@ fn unmute(context: &Context, message: &Message, _args: Vec<String>) { } /// Checks that a message successfully sent; if not, then logs why to stdout. +#[cfg(feature = "voice")] fn check_msg(result: SerenityResult<Message>) { if let Err(why) = result { println!("Error sending message: {:?}", why); diff --git a/src/client/context.rs b/src/client/context.rs index 678d362..d648888 100644 --- a/src/client/context.rs +++ b/src/client/context.rs @@ -1,5 +1,6 @@ use serde_json::builder::ObjectBuilder; use std::collections::HashMap; +use std::fmt::Write as FmtWrite; use std::io::Read; use std::sync::{Arc, Mutex}; use super::gateway::Shard; @@ -127,9 +128,7 @@ impl Context { return Err(Error::Client(ClientError::InvalidOperationAsBot)); } - let code = utils::parse_invite(invite); - - rest::accept_invite(code) + rest::accept_invite(utils::parse_invite(invite)) } /// Mark a [`Channel`] as being read up to a certain [`Message`]. @@ -481,7 +480,13 @@ impl Context { where F: FnOnce(EditRole) -> EditRole, G: Into<GuildId> { let id = guild_id.into().0; - // The API only allows creating an empty role. + // The API only allows creating an empty role, which must then be + // edited. + // + // Note to self: [this] issue/proposal may make this not require an + // edit. + // + // [this]: http://github.com/hammerandchisel/discord-api-docs/issues/156 let role = try!(rest::create_role(id)); let map = f(EditRole::default()).0.build(); @@ -544,9 +549,7 @@ impl Context { /// [`Invite::delete`]: ../model/struct.Invite.html#method.delete /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html pub fn delete_invite(&self, invite: &str) -> Result<Invite> { - let code = utils::parse_invite(invite); - - rest::delete_invite(code) + rest::delete_invite(utils::parse_invite(invite)) } /// Deletes a [`Message`] given its Id. @@ -859,7 +862,7 @@ impl Context { let guild_id = guild_id.into(); let role_id = role_id.into(); - feature_cache! {{ + let map = feature_cache! {{ let cache = CACHE.read().unwrap(); let role = if let Some(role) = { @@ -870,14 +873,12 @@ impl Context { return Err(Error::Client(ClientError::RecordNotFound)); }; - let map = f(EditRole::new(role)).0.build(); - - rest::edit_role(guild_id.0, role_id.0, map) + f(EditRole::new(role)).0.build() } else { - let map = f(EditRole::default()).0.build(); + f(EditRole::default()).0.build() + }}; - rest::edit_role(guild_id.0, role_id.0, map) - }} + rest::edit_role(guild_id.0, role_id.0, map) } /// Edit a message given its Id and the Id of the channel it belongs to. @@ -1061,22 +1062,19 @@ impl Context { where C: Into<ChannelId>, F: FnOnce(GetMessages) -> GetMessages { let query = { let mut map = f(GetMessages::default()).0; - let mut query = format!("?limit={}", - map.remove("limit").unwrap_or(50)); + let mut query = String::new(); + try!(write!(query, "?limit={}", map.remove("limit").unwrap_or(50))); if let Some(after) = map.remove("after") { - query.push_str("&after="); - query.push_str(&after.to_string()); + try!(write!(query, "&after={}", after)); } if let Some(around) = map.remove("around") { - query.push_str("&around="); - query.push_str(&around.to_string()); + try!(write!(query, "&around={}", around)); } if let Some(before) = map.remove("before") { - query.push_str("&before="); - query.push_str(&before.to_string()); + try!(write!(query, "&before={}", before)); } query @@ -1118,7 +1116,11 @@ impl Context { M: Into<MessageId>, R: Into<ReactionType>, U: Into<UserId> { - let limit = limit.map(|x| if x > 100 { 100 } else { x }).unwrap_or(50); + let limit = limit.map(|x| if x > 100 { + 100 + } else { + x + }).unwrap_or(50); rest::get_reaction_users(channel_id.into().0, message_id.into().0, diff --git a/src/client/gateway/prep.rs b/src/client/gateway/prep.rs index 2ca435b..6aa40f3 100644 --- a/src/client/gateway/prep.rs +++ b/src/client/gateway/prep.rs @@ -21,10 +21,10 @@ use ::model::event::{Event, GatewayEvent, ReadyEvent}; #[inline] pub fn parse_ready(event: GatewayEvent, - tx: &MpscSender<GatewayStatus>, - receiver: &mut Receiver<WebSocketStream>, - identification: Value) - -> Result<(ReadyEvent, u64)> { + tx: &MpscSender<GatewayStatus>, + receiver: &mut Receiver<WebSocketStream>, + identification: Value) + -> Result<(ReadyEvent, u64)> { match event { GatewayEvent::Dispatch(seq, Event::Ready(event)) => { Ok((event, seq)) @@ -60,7 +60,7 @@ pub fn identify(token: &str, shard_info: Option<[u8; 2]>) -> Value { object = identify_compression(object) .insert("large_threshold", 250) // max value .insert_object("properties", |object| object - .insert("$browser", "Feature-full and ergonomic discord rust library") + .insert("$browser", "Ergonomic and high-level Rust library") .insert("$device", "serenity") .insert("$os", env::consts::OS) .insert("$referrer", "") @@ -70,10 +70,9 @@ pub fn identify(token: &str, shard_info: Option<[u8; 2]>) -> Value { .insert("v", constants::GATEWAY_VERSION); if let Some(shard_info) = shard_info { - object = object - .insert_array("shard", |a| a - .push(shard_info[0]) - .push(shard_info[1])); + object = object.insert_array("shard", |a| a + .push(shard_info[0]) + .push(shard_info[1])); } object @@ -97,8 +96,8 @@ pub fn build_gateway_url(base: &str) -> Result<RequestUrl> { } pub fn keepalive(interval: u64, - mut sender: Sender<WebSocketStream>, - channel: MpscReceiver<GatewayStatus>) { + mut sender: Sender<WebSocketStream>, + channel: MpscReceiver<GatewayStatus>) { let mut base_interval = Duration::milliseconds(interval as i64); let mut next_tick = time::get_time() + base_interval; diff --git a/src/client/gateway/shard.rs b/src/client/gateway/shard.rs index 13a8c06..1464bd3 100644 --- a/src/client/gateway/shard.rs +++ b/src/client/gateway/shard.rs @@ -135,9 +135,9 @@ impl Shard { // Parse READY let event = try!(receiver.recv_json(GatewayEvent::decode)); let (ready, sequence) = try!(prep::parse_ready(event, - &tx, - &mut receiver, - identification)); + &tx, + &mut receiver, + identification)); Ok((feature_voice! {{ Shard { @@ -512,8 +512,8 @@ impl Shard { let msg = ObjectBuilder::new() .insert("op", OpCode::StatusUpdate.num()) .insert_object("d", move |mut object| { - object = object.insert("since", 0) - .insert("afk", afk) + object = object.insert("afk", afk) + .insert("since", 0) .insert("status", status.name()); match game.as_ref() { diff --git a/src/client/mod.rs b/src/client/mod.rs index 41e982a..92b071e 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -812,8 +812,6 @@ impl Client { } } - // How to avoid the problem while still working on other parts of the - // library 101 loop { thread::sleep(Duration::from_secs(1)); } diff --git a/src/client/rest/mod.rs b/src/client/rest/mod.rs index 84c7c00..fcd29e3 100644 --- a/src/client/rest/mod.rs +++ b/src/client/rest/mod.rs @@ -41,6 +41,7 @@ use serde_json::builder::ObjectBuilder; use serde_json; use std::collections::BTreeMap; use std::default::Default; +use std::fmt::Write as FmtWrite; use std::io::{ErrorKind as IoErrorKind, Read}; use std::sync::{Arc, Mutex}; use ::constants; @@ -1138,7 +1139,7 @@ pub fn get_message(channel_id: u64, message_id: u64) /// Gets X messages from a channel. pub fn get_messages(channel_id: u64, query: &str) -> Result<Vec<Message>> { - let url = format!(api_concat!("/channels/{}/messages{}"), + let url = format!(api!("/channels/{}/messages{}"), channel_id, query); let client = HyperClient::new(); @@ -1172,8 +1173,7 @@ pub fn get_reaction_users(channel_id: u64, limit); if let Some(user_id) = after { - uri.push_str("&after="); - uri.push_str(&user_id.to_string()); + try!(write!(uri, "&after={}", user_id)); } let response = request!(Route::ChannelsIdMessagesIdReactionsUserIdType(channel_id), @@ -1317,7 +1317,7 @@ pub fn send_file<R: Read>(channel_id: u64, filename: &str, map: BTreeMap<String, Value>) -> Result<Message> { - let uri = format!(api_concat!("/channels/{}/messages"), channel_id); + let uri = format!(api!("/channels/{}/messages"), channel_id); let url = match Url::parse(&uri) { Ok(url) => url, Err(_why) => return Err(Error::Url(uri)), diff --git a/src/constants.rs b/src/constants.rs index 68f6524..50dd722 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,5 +1,3 @@ -use ::internal::prelude::*; - /// The gateway version used by the library. The gateway URI is retrieved via /// the REST API. pub const GATEWAY_VERSION: u8 = 6; @@ -10,94 +8,6 @@ pub const MESSAGE_CODE_LIMIT: u16 = 2000; /// [UserAgent]: ../hyper/header/struct.UserAgent.html pub const USER_AGENT: &'static str = concat!("DiscordBot (https://github.com/zeyla/serenity.rs, ", env!("CARGO_PKG_VERSION"), ")"); -#[allow(dead_code)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum ErrorCode { - BotsCannotUse, - CannotSendEmptyMessage, - CannotSendMessagesInVoice, - CannotSendMessagesToUser, - ChannelVerificationTooHigh, - EditByOtherAuthor, - EmbedDisabled, - InvalidAccountType, - InvalidAuthToken, - InvalidBulkDeleteCount, - InvalidDMChannelAction, - InvalidOauthState, - InvalidPinChannel, - MaxFriendsReached, - MaxGuildsReached, - MaxPinsReached, - MaxRolesReached, - MissingAccess, - MissingPermissions, - NoteTooLong, - Oauth2ApplicationLacksBot, - Oauth2ApplicationLimitReached, - OnlyBotsCanUse, - ReactionBlocked, - TooManyReactions, - Unauthorized, - UnknownAccount, - UnknownApplication, - UnknownChannel, - UnknownGuild, - UnknownEmoji, - UnknownIntegration, - UnknownInvite, - UnknownMember, - UnknownMessage, - UnknownOverwrite, - UnknownProvider, - UnknownRole, - UnknownToken, - UnknownUser, -} - -map_nums! { ErrorCode; - BotsCannotUse 20001, - CannotSendEmptyMessage 50006, - CannotSendMessagesInVoice 50008, - CannotSendMessagesToUser 50007, - ChannelVerificationTooHigh 50009, - EditByOtherAuthor 50005, - EmbedDisabled 50004, - InvalidAccountType 50002, - InvalidAuthToken 50014, - InvalidBulkDeleteCount 50016, - InvalidDMChannelAction 50003, - InvalidOauthState 50012, - InvalidPinChannel 50019, - MaxFriendsReached 30002, - MaxGuildsReached 30001, - MaxPinsReached 30003, - MaxRolesReached 30005, - MissingAccess 50001, - MissingPermissions 500013, - NoteTooLong 50015, - Oauth2ApplicationLacksBot 50010, - Oauth2ApplicationLimitReached 50011, - OnlyBotsCanUse 20002, - ReactionBlocked 90001, - TooManyReactions 30010, - Unauthorized 40001, - UnknownAccount 10001, - UnknownApplication 10002, - UnknownChannel 10003, - UnknownEmoji 10014, - UnknownGuild 10004, - UnknownIntegration 10005, - UnknownInvite 10006, - UnknownMember 10007, - UnknownMessage 10008, - UnknownOverwrite 10009, - UnknownProvider 10010, - UnknownRole 10011, - UnknownToken 10012, - UnknownUser 10013, -} - #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum OpCode { Event, @@ -116,21 +26,45 @@ pub enum OpCode { SyncCall, } -map_nums! { OpCode; - Event 0, - Heartbeat 1, - Identify 2, - StatusUpdate 3, - VoiceStateUpdate 4, - VoiceServerPing 5, - Resume 6, - Reconnect 7, - GetGuildMembers 8, - InvalidSession 9, - Hello 10, - HeartbeatAck 11, - SyncGuild 12, - SyncCall 13, +impl OpCode { + pub fn from_num(num: u64) -> Option<Self> { + match num { + 0 => Some(OpCode::Event), + 1 => Some(OpCode::Heartbeat), + 2 => Some(OpCode::Identify), + 3 => Some(OpCode::StatusUpdate), + 4 => Some(OpCode::VoiceStateUpdate), + 5 => Some(OpCode::VoiceServerPing), + 6 => Some(OpCode::Resume), + 7 => Some(OpCode::Reconnect), + 8 => Some(OpCode::GetGuildMembers), + 9 => Some(OpCode::InvalidSession), + 10 => Some(OpCode::Hello), + 11 => Some(OpCode::HeartbeatAck), + 12 => Some(OpCode::SyncGuild), + 13 => Some(OpCode::SyncCall), + _ => None, + } + } + + pub fn num(&self) -> u64 { + match *self { + OpCode::Event => 0, + OpCode::Heartbeat => 1, + OpCode::Identify => 2, + OpCode::StatusUpdate => 3, + OpCode::VoiceStateUpdate => 4, + OpCode::VoiceServerPing => 5, + OpCode::Resume => 6, + OpCode::Reconnect => 7, + OpCode::GetGuildMembers => 8, + OpCode::InvalidSession => 9, + OpCode::Hello => 10, + OpCode::HeartbeatAck => 11, + OpCode::SyncGuild => 12, + OpCode::SyncCall => 13, + } + } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -144,12 +78,29 @@ pub enum VoiceOpCode { Speaking, } -map_nums! { VoiceOpCode; - Identify 0, - SelectProtocol 1, - Hello 2, - KeepAlive 3, - SessionDescription 4, - Speaking 5, - Heartbeat 8, +impl VoiceOpCode { + pub fn from_num(num: u64) -> Option<Self> { + match num { + 0 => Some(VoiceOpCode::Identify), + 1 => Some(VoiceOpCode::SelectProtocol), + 2 => Some(VoiceOpCode::Hello), + 3 => Some(VoiceOpCode::KeepAlive), + 4 => Some(VoiceOpCode::SessionDescription), + 5 => Some(VoiceOpCode::Speaking), + 8 => Some(VoiceOpCode::Heartbeat), + _ => None, + } + } + + pub fn num(&self) -> u64 { + match *self { + VoiceOpCode::Identify => 0, + VoiceOpCode::SelectProtocol => 1, + VoiceOpCode::Hello => 2, + VoiceOpCode::KeepAlive => 3, + VoiceOpCode::SessionDescription => 4, + VoiceOpCode::Speaking => 5, + VoiceOpCode::Heartbeat => 8, + } + } } diff --git a/src/error.rs b/src/error.rs index 94958fd..eaa2e13 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,9 +1,9 @@ -use std::io::Error as IoError; -use std::error::Error as StdError; -use std::fmt::{self, Display}; use hyper::Error as HyperError; use serde_json::Error as JsonError; use serde_json::Value; +use std::io::Error as IoError; +use std::error::Error as StdError; +use std::fmt::{self, Display, Error as FormatError}; use websocket::result::WebSocketError; use ::client::gateway::GatewayError; use ::client::ClientError; @@ -47,6 +47,8 @@ pub enum Error { Gateway(GatewayError), /// An error while decoding a payload. Decode(&'static str, Value), + /// There was an error with a format. + Format(FormatError), /// An error from the `hyper` crate. Hyper(HyperError), /// An `std::io` error. @@ -73,6 +75,12 @@ pub enum Error { Voice(VoiceError), } +impl From<FormatError> for Error { + fn from(e: FormatError) -> Error { + Error::Format(e) + } +} + impl From<IoError> for Error { fn from(e: IoError) -> Error { Error::Io(e) @@ -122,8 +130,9 @@ impl StdError for Error { fn description(&self) -> &str { match *self { Error::Client(_) => "Client refused a request", - Error::Gateway(ref _inner) => "Gateway error", Error::Decode(msg, _) | Error::Other(msg) => msg, + Error::Format(ref inner) => inner.description(), + Error::Gateway(ref _inner) => "Gateway error", Error::Hyper(ref inner) => inner.description(), Error::Io(ref inner) => inner.description(), Error::Json(ref inner) => inner.description(), diff --git a/src/ext/cache/mod.rs b/src/ext/cache/mod.rs index 0981cf3..2272178 100644 --- a/src/ext/cache/mod.rs +++ b/src/ext/cache/mod.rs @@ -244,7 +244,7 @@ impl Cache { pub fn all_guilds(&self) -> Vec<GuildId> { self.guilds .values() - .map(|s| s.id) + .map(|g| g.id) .chain(self.unavailable_guilds.iter().cloned()) .collect() } @@ -259,7 +259,7 @@ impl Cache { guild.id }) - .collect::<Vec<_>>() + .collect::<Vec<GuildId>>() } /// Retrieves a reference to a [`Call`] from the cache based on the @@ -324,8 +324,9 @@ impl Cache { /// let channel = match cache.get_guild_channel(message.channel_id) { /// Some(channel) => channel, /// None => { - /// context.say("Could not find guild's channel data") - /// .map_err(|why| println!("Error sending message: {:?}", why)); + /// if let Err(why) = context.say("Could not find guild's channel data") { + /// println!("Error sending message: {:?}", why); + /// } /// /// return; /// }, @@ -406,7 +407,7 @@ impl Cache { guild.members.get(&user_id.into()) }).and_then(|x| match x { Some(x) => Some(x), - _ => None, + None => None, }) } @@ -422,130 +423,6 @@ impl Cache { } } - /// Update the cache according to the changes described in the given event. - #[allow(cyclomatic_complexity)] - #[allow(unneeded_field_pattern)] - #[doc(hidden)] - pub fn update(&mut self, event: &Event) { - match *event { - Event::CallCreate(ref event) => { - self.update_with_call_create(event); - }, - Event::CallDelete(ref event) => { - self.update_with_call_delete(event); - }, - Event::CallUpdate(ref event) => { - self.update_with_call_update(event, false); - }, - Event::ChannelCreate(ref event) => { - self.update_with_channel_create(event); - }, - Event::ChannelDelete(ref event) => { - self.update_with_channel_delete(event); - }, - Event::ChannelPinsUpdate(ref event) => { - self.update_with_channel_pins_update(event); - }, - Event::ChannelRecipientAdd(ref event) => { - self.update_with_channel_recipient_add(event); - }, - Event::ChannelRecipientRemove(ref event) => { - self.update_with_channel_recipient_remove(event); - }, - Event::ChannelUpdate(ref event) => { - self.update_with_channel_update(event); - }, - Event::GuildCreate(ref event) => { - self.update_with_guild_create(event); - }, - Event::GuildDelete(ref event) => { - self.update_with_guild_delete(event); - }, - Event::GuildEmojisUpdate(ref event) => { - self.update_with_guild_emojis_update(event); - }, - Event::GuildMemberAdd(ref event) => { - self.update_with_guild_member_add(event); - }, - Event::GuildMemberRemove(ref event) => { - self.update_with_guild_member_remove(event); - }, - Event::GuildMemberUpdate(ref event) => { - self.update_with_guild_member_update(event); - }, - Event::GuildMembersChunk(ref event) => { - self.update_with_guild_members_chunk(event); - }, - Event::GuildRoleCreate(ref event) => { - self.update_with_guild_role_create(event); - }, - Event::GuildRoleDelete(ref event) => { - self.update_with_guild_role_delete(event); - }, - Event::GuildRoleUpdate(ref event) => { - self.update_with_guild_role_update(event); - }, - Event::GuildSync(ref event) => { - self.update_with_guild_sync(event); - }, - Event::GuildUnavailable(ref event) => { - self.update_with_guild_unavailable(event); - }, - Event::GuildUpdate(ref event) => { - self.update_with_guild_update(event); - }, - Event::PresencesReplace(ref event) => { - self.update_with_presences_replace(event); - }, - Event::PresenceUpdate(ref event) => { - self.update_with_presence_update(event); - }, - Event::Ready(ref event) => { - self.update_with_ready(event); - }, - Event::RelationshipAdd(ref event) => { - self.update_with_relationship_add(event); - }, - Event::RelationshipRemove(ref event) => { - self.update_with_relationship_remove(event); - }, - Event::UserGuildSettingsUpdate(ref event) => { - self.update_with_user_guild_settings_update(event); - }, - Event::UserNoteUpdate(ref event) => { - self.update_with_user_note_update(event); - }, - Event::UserSettingsUpdate(ref event) => { - self.update_with_user_settings_update(event, false); - }, - Event::UserUpdate(ref event) => { - self.update_with_user_update(event); - }, - Event::VoiceStateUpdate(ref event) => { - self.update_with_voice_state_update(event); - }, - Event::ChannelPinsAck(_) | - Event::FriendSuggestionCreate(_) | - Event::FriendSuggestionDelete(_) | - Event::GuildBanAdd(_) | - Event::GuildBanRemove(_) | - Event::GuildIntegrationsUpdate(_) | - Event::MessageAck(_) | - Event::MessageCreate(_) | - Event::MessageDelete(_) | - Event::MessageDeleteBulk(_) | - Event::MessageUpdate(_) | - Event::ReactionAdd(_) | - Event::ReactionRemove(_) | - Event::ReactionRemoveAll(_) | - Event::Resumed(_) | - Event::TypingStart(_) | - Event::VoiceServerUpdate(_) | - Event::WebhookUpdate(_) | - Event::Unknown(_) => {}, - } - } - #[doc(hidden)] pub fn update_with_call_create(&mut self, event: &CallCreateEvent) { match self.calls.entry(event.call.channel_id) { @@ -604,12 +481,10 @@ impl Cache { guild.channels.insert(channel.id, channel.clone()) }); - let ch = match ch { - Some(Some(ch)) => Some(ch), + match ch { + Some(Some(ch)) => Some(Channel::Guild(ch)), _ => None, - }; - - ch.map(Channel::Guild) + } }, } } @@ -695,7 +570,8 @@ impl Cache { let dest = e.get_mut(); if group.recipients.is_empty() { - let recipients = mem::replace(&mut dest.recipients, HashMap::new()); + let recipients = mem::replace(&mut dest.recipients, + HashMap::new()); dest.clone_from(group); @@ -1075,8 +951,8 @@ impl Cache { let finding = call.voice_states .get_mut(&event.voice_state.user_id); - if let Some(grp_state) = finding { - grp_state.clone_from(&event.voice_state); + if let Some(group_state) = finding { + group_state.clone_from(&event.voice_state); return; } diff --git a/src/ext/framework/command.rs b/src/ext/framework/command.rs index e0a4616..46338a1 100644 --- a/src/ext/framework/command.rs +++ b/src/ext/framework/command.rs @@ -1,5 +1,5 @@ use std::sync::Arc; -use super::{CommandType, Configuration}; +use super::Configuration; use ::client::Context; use ::model::Message; @@ -8,15 +8,14 @@ pub type Command = Fn(&Context, &Message, Vec<String>) + Send + Sync; #[doc(hidden)] pub type InternalCommand = Arc<Command>; -pub fn positions(content: &str, conf: &Configuration) - -> Option<(Vec<usize>, CommandType)> { +pub fn positions(content: &str, conf: &Configuration) -> Option<Vec<usize>> { if let Some(ref prefix) = conf.prefix { // Find out if they were mentioned. If not, determine if the prefix // was used. If not, return None. - let (mut positions, kind) = if let Some(mention_end) = find_mention_end(content, conf) { - (vec![mention_end], CommandType::Mention) + let mut positions = if let Some(mention_end) = find_mention_end(content, conf) { + vec![mention_end] } else if content.starts_with(prefix) { - (vec![prefix.len()], CommandType::Prefix) + vec![prefix.len()] } else { return None; }; @@ -29,7 +28,7 @@ pub fn positions(content: &str, conf: &Configuration) positions.insert(0, pos + 1); } - Some((positions, kind)) + Some(positions) } else if conf.on_mention.is_some() { match find_mention_end(content, conf) { Some(mention_end) => { @@ -39,7 +38,7 @@ pub fn positions(content: &str, conf: &Configuration) positions.insert(0, mention_end + 1); } - Some((positions, CommandType::Mention)) + Some(positions) }, None => None, } diff --git a/src/ext/framework/mod.rs b/src/ext/framework/mod.rs index e79bd18..a470550 100644 --- a/src/ext/framework/mod.rs +++ b/src/ext/framework/mod.rs @@ -125,34 +125,6 @@ macro_rules! command { }; } -/// The type of command being received. -/// -/// The [`Mention`] variant is emitted if the bot is being commanded via a -/// mention (`<@USER_ID>` or `<@!USER_ID>`). This can only be emitted if -/// [`Configuration::on_mention`] is set to `true`. -/// -/// The [`Prefix`] variant is emitted if a message starts with the prefix set -/// via [`Configuration::prefix`]. -/// -/// [`Mention`]: #variant.Mention -/// [`Prefix`]: #variant.Prefix -// This is public due to being leaked by [`command::positions`], which is used -// in [`Framework::dispatch`]. It therefore is hidden from the docs, due to -// having no use to users. -// -// [`Framework::dispatch`]: struct.Framework.html#method.dispatch -// [`command::positions`]: command/fn.positions.html -#[derive(Clone, Copy, Debug)] -#[doc(hidden)] -pub enum CommandType { - /// This is emitted if the bot is being commanded via a mention - /// (`<@USER_ID>` or `<@!USER_ID>`). This can only be emitted if - /// [`Configuration::on_mention`] is set to `true`. - Mention, - None, - Prefix, -} - /// A utility for easily managing dispatches to commands. /// /// Refer to the [module-level documentation] for more information. @@ -214,7 +186,6 @@ impl Framework { pub fn configure<F>(mut self, f: F) -> Self where F: FnOnce(Configuration) -> Configuration { self.configuration = f(self.configuration); - self.initialized = true; self } @@ -224,7 +195,7 @@ impl Framework { let res = command::positions(&message.content, &self.configuration); let positions = match res { - Some((positions, _kind)) => positions, + Some(positions) => positions, None => return, }; @@ -358,7 +329,6 @@ impl Framework { where F: Fn(&Context, &Message) -> bool + Send + Sync + 'static, S: Into<String> { self.checks.insert(command.into(), Arc::new(check)); - self.initialized = true; self } diff --git a/src/ext/voice/audio.rs b/src/ext/voice/audio.rs index 814cd69..ea8c87a 100644 --- a/src/ext/voice/audio.rs +++ b/src/ext/voice/audio.rs @@ -1,5 +1,3 @@ -use ::model::UserId; - pub const HEADER_LEN: usize = 12; pub const SAMPLE_RATE: u32 = 48000; diff --git a/src/ext/voice/connection.rs b/src/ext/voice/connection.rs index 8f1821d..f88a3ff 100644 --- a/src/ext/voice/connection.rs +++ b/src/ext/voice/connection.rs @@ -26,7 +26,7 @@ use websocket::stream::WebSocketStream; use ::internal::prelude::*; use ::internal::ws_impl::{ReceiverExt, SenderExt}; use ::internal::Timer; -use ::model::VoiceEvent; +use ::model::event::VoiceEvent; enum ReceiverStatus { Udp(Vec<u8>), @@ -165,11 +165,11 @@ impl Connection { } #[allow(unused_variables)] - pub fn update(&mut self, - source: &mut Option<Box<AudioSource>>, - receiver: &mut Option<Box<AudioReceiver>>, - audio_timer: &mut Timer) - -> Result<()> { + pub fn cycle(&mut self, + source: &mut Option<Box<AudioSource>>, + receiver: &mut Option<Box<AudioReceiver>>, + audio_timer: &mut Timer) + -> Result<()> { let mut buffer = [0i16; 960 * 2]; let mut packet = [0u8; 512]; let mut nonce = secretbox::Nonce([0; 24]); @@ -230,7 +230,7 @@ impl Connection { try!(self.sender.send_json(&payload::build_keepalive())); } - // Send the UDP keepalive if it's time + // Send UDP keepalive if it's time if self.audio_timer.check() { let mut bytes = [0; 4]; try!((&mut bytes[..]).write_u32::<BigEndian>(self.ssrc)); @@ -286,7 +286,7 @@ impl Connection { nonce.0[..HEADER_LEN].clone_from_slice(&packet[..HEADER_LEN]); - let extent = packet.len() - 16; + let sl_index = packet.len() - 16; let buffer_len = if self.encoder_stereo { 960 * 2 } else { @@ -294,7 +294,7 @@ impl Connection { }; let len = try!(self.encoder.encode(&buffer[..buffer_len], - &mut packet[HEADER_LEN..extent])); + &mut packet[HEADER_LEN..sl_index])); let crypted = { let slice = &packet[HEADER_LEN..HEADER_LEN + len]; secretbox::seal(slice, &nonce, &self.key) diff --git a/src/ext/voice/threading.rs b/src/ext/voice/threading.rs index bbbffd1..8d3599b 100644 --- a/src/ext/voice/threading.rs +++ b/src/ext/voice/threading.rs @@ -66,11 +66,11 @@ fn runner(rx: MpscReceiver<Status>) { // another event. let error = match connection.as_mut() { Some(connection) => { - let update = connection.update(&mut sender, - &mut receiver, - &mut timer); + let cycle = connection.cycle(&mut sender, + &mut receiver, + &mut timer); - match update { + match cycle { Ok(()) => false, Err(why) => { error!("[Voice] Error updating connection: {:?}", why); diff --git a/src/internal/ws_impl.rs b/src/internal/ws_impl.rs index ea327fd..72af52c 100644 --- a/src/internal/ws_impl.rs +++ b/src/internal/ws_impl.rs @@ -22,11 +22,9 @@ impl ReceiverExt for Receiver<WebSocketStream> { let message: WsMessage = try!(self.recv_message()); if message.opcode == WsType::Close { - let representation = String::from_utf8_lossy(&message.payload) - .into_owned(); + let r = String::from_utf8_lossy(&message.payload).into_owned(); - Err(Error::Gateway(GatewayError::Closed(message.cd_status_code, - representation))) + Err(Error::Gateway(GatewayError::Closed(message.cd_status_code, r))) } else if message.opcode == WsType::Binary || message.opcode == WsType::Text { let json: Value = if message.opcode == WsType::Binary { try!(serde_json::from_reader(ZlibDecoder::new(&message.payload[..]))) @@ -34,17 +32,20 @@ impl ReceiverExt for Receiver<WebSocketStream> { try!(serde_json::from_reader(&message.payload[..])) }; - decode(json).map_err(|err| { - warn!("Error decoding: {}", - String::from_utf8_lossy(&message.payload)); + match decode(json) { + Ok(v) => Ok(v), + Err(why) => { + let s = String::from_utf8_lossy(&message.payload); - err - }) + warn!("Error decoding: {}", s); + + Err(why) + } + } } else { - let representation = String::from_utf8_lossy(&message.payload) - .into_owned(); + let r = String::from_utf8_lossy(&message.payload).into_owned(); - Err(Error::Gateway(GatewayError::Closed(None, representation))) + Err(Error::Gateway(GatewayError::Closed(None, r))) } } } @@ -76,7 +76,7 @@ //! [`validate_token`]: client/fn.validate_token.html #![allow(doc_markdown, inline_always, unknown_lints)] #![doc(html_logo_url = "https://docs.austinhellyer.me/serenity.rs/docs_header.png")] -#![warn(dead_code, enum_glob_use, if_not_else)] +#![warn(enum_glob_use, if_not_else)] #[macro_use] extern crate bitflags; diff --git a/src/model/channel.rs b/src/model/channel.rs index 33e6044..13cf506 100644 --- a/src/model/channel.rs +++ b/src/model/channel.rs @@ -6,7 +6,6 @@ use super::utils::{ into_string, opt, remove, - warn_field, }; use super::*; use ::constants; @@ -343,7 +342,7 @@ impl Group { /// Returns the formatted URI of the group's icon if one exists. pub fn icon_url(&self) -> Option<String> { self.icon.as_ref().map(|icon| - format!(cdn_concat!("/channel-icons/{}/{}.jpg"), self.channel_id, icon)) + format!(cdn!("/channel-icons/{}/{}.jpg"), self.channel_id, icon)) } /// Leaves the group. @@ -639,8 +638,7 @@ impl Message { }} let mut gen = format!("{}", self.author.mention()); - gen.push(':'); - gen.push(' '); + gen.push_str(": "); gen.push_str(content); let map = ObjectBuilder::new() @@ -690,7 +688,7 @@ impl PermissionOverwrite { _ => return Err(Error::Decode("Expected valid PermissionOverwrite type", Value::String(kind))), }; - missing!(map, PermissionOverwrite { + Ok(PermissionOverwrite { kind: kind, allow: try!(remove(&mut map, "allow").and_then(Permissions::decode)), deny: try!(remove(&mut map, "deny").and_then(Permissions::decode)), @@ -711,7 +709,7 @@ impl PrivateChannel { let mut recipients = try!(decode_array(try!(remove(&mut map, "recipients")), User::decode)); - missing!(map, PrivateChannel { + Ok(PrivateChannel { id: try!(remove(&mut map, "id").and_then(ChannelId::decode)), kind: try!(remove(&mut map, "type").and_then(ChannelType::decode)), last_message_id: try!(opt(&mut map, "last_message_id", MessageId::decode)), @@ -858,7 +856,8 @@ impl GuildChannel { #[doc(hidden)] pub fn decode_guild(value: Value, guild_id: GuildId) -> Result<GuildChannel> { let mut map = try!(into_map(value)); - missing!(map, GuildChannel { + + Ok(GuildChannel { id: try!(remove(&mut map, "id").and_then(ChannelId::decode)), name: try!(remove(&mut map, "name").and_then(into_string)), guild_id: guild_id, diff --git a/src/model/event.rs b/src/model/event.rs index 694ebf1..d459503 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap}; use super::utils::*; use super::*; -use ::constants::OpCode; +use ::constants::{OpCode, VoiceOpCode}; use ::internal::prelude::*; use ::utils::decode_array; @@ -489,11 +489,11 @@ impl Event { call: try!(Call::decode(Value::Object(value))), })) } else if kind == "CALL_DELETE" { - missing!(value, Event::CallDelete(CallDeleteEvent { + Ok(Event::CallDelete(CallDeleteEvent { channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), })) } else if kind == "CALL_UPDATE" { - missing!(value, Event::CallUpdate(CallUpdateEvent { + Ok(Event::CallUpdate(CallUpdateEvent { channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), message_id: try!(remove(&mut value, "message_id").and_then(MessageId::decode)), region: try!(remove(&mut value, "region").and_then(into_string)), @@ -508,22 +508,22 @@ impl Event { channel: try!(Channel::decode(Value::Object(value))), })) } else if kind == "CHANNEL_PINS_ACK" { - missing!(value, Event::ChannelPinsAck(ChannelPinsAckEvent { + Ok(Event::ChannelPinsAck(ChannelPinsAckEvent { channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), timestamp: try!(remove(&mut value, "timestamp").and_then(into_string)), })) } else if kind == "CHANNEL_PINS_UPDATE" { - missing!(value, Event::ChannelPinsUpdate(ChannelPinsUpdateEvent { + Ok(Event::ChannelPinsUpdate(ChannelPinsUpdateEvent { channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), last_pin_timestamp: try!(opt(&mut value, "last_pin_timestamp", into_string)), })) } else if kind == "CHANNEL_RECIPIENT_ADD" { - missing!(value, Event::ChannelRecipientAdd(ChannelRecipientAddEvent { + Ok(Event::ChannelRecipientAdd(ChannelRecipientAddEvent { channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), user: try!(remove(&mut value, "user").and_then(User::decode)), })) } else if kind == "CHANNEL_RECIPIENT_REMOVE" { - missing!(value, Event::ChannelRecipientRemove(ChannelRecipientRemoveEvent { + Ok(Event::ChannelRecipientRemove(ChannelRecipientRemoveEvent { channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), user: try!(remove(&mut value, "user").and_then(User::decode)), })) @@ -532,21 +532,21 @@ impl Event { channel: try!(Channel::decode(Value::Object(value))), })) } else if kind == "FRIEND_SUGGESTION_CREATE" { - missing!(value, Event::FriendSuggestionCreate(FriendSuggestionCreateEvent { + Ok(Event::FriendSuggestionCreate(FriendSuggestionCreateEvent { reasons: try!(decode_array(try!(remove(&mut value, "reasons")), SuggestionReason::decode)), suggested_user: try!(remove(&mut value, "suggested_user").and_then(User::decode)), })) } else if kind == "FRIEND_SUGGESTION_DELETE" { - missing!(value, Event::FriendSuggestionDelete(FriendSuggestionDeleteEvent { + Ok(Event::FriendSuggestionDelete(FriendSuggestionDeleteEvent { suggested_user_id: try!(remove(&mut value, "suggested_user_id").and_then(UserId::decode)), })) } else if kind == "GUILD_BAN_ADD" { - missing!(value, Event::GuildBanAdd(GuildBanAddEvent { + Ok(Event::GuildBanAdd(GuildBanAddEvent { guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), user: try!(remove(&mut value, "user").and_then(User::decode)), })) } else if kind == "GUILD_BAN_REMOVE" { - missing!(value, Event::GuildBanRemove(GuildBanRemoveEvent { + Ok(Event::GuildBanRemove(GuildBanRemoveEvent { guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), user: try!(remove(&mut value, "user").and_then(User::decode)), })) @@ -571,12 +571,12 @@ impl Event { })) } } else if kind == "GUILD_EMOJIS_UPDATE" { - missing!(value, Event::GuildEmojisUpdate(GuildEmojisUpdateEvent { + Ok(Event::GuildEmojisUpdate(GuildEmojisUpdateEvent { emojis: try!(remove(&mut value, "emojis").and_then(decode_emojis)), guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), })) } else if kind == "GUILD_INTEGRATIONS_UPDATE" { - missing!(value, Event::GuildIntegrationsUpdate(GuildIntegrationsUpdateEvent { + Ok(Event::GuildIntegrationsUpdate(GuildIntegrationsUpdateEvent { guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), })) } else if kind == "GUILD_MEMBER_ADD" { @@ -585,39 +585,39 @@ impl Event { member: try!(Member::decode(Value::Object(value))), })) } else if kind == "GUILD_MEMBER_REMOVE" { - missing!(value, Event::GuildMemberRemove(GuildMemberRemoveEvent { + Ok(Event::GuildMemberRemove(GuildMemberRemoveEvent { guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), user: try!(remove(&mut value, "user").and_then(User::decode)), })) } else if kind == "GUILD_MEMBER_UPDATE" { - missing!(value, Event::GuildMemberUpdate(GuildMemberUpdateEvent { + Ok(Event::GuildMemberUpdate(GuildMemberUpdateEvent { guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), nick: try!(opt(&mut value, "nick", into_string)), roles: try!(decode_array(try!(remove(&mut value, "roles")), RoleId::decode)), user: try!(remove(&mut value, "user").and_then(User::decode)), })) } else if kind == "GUILD_MEMBERS_CHUNK" { - missing!(value, Event::GuildMembersChunk(GuildMembersChunkEvent { + Ok(Event::GuildMembersChunk(GuildMembersChunkEvent { guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), members: try!(remove(&mut value, "members").and_then(decode_members)), })) } else if kind == "GUILD_ROLE_CREATE" { - missing!(value, Event::GuildRoleCreate(GuildRoleCreateEvent { + Ok(Event::GuildRoleCreate(GuildRoleCreateEvent { guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), role: try!(remove(&mut value, "role").and_then(Role::decode)), })) } else if kind == "GUILD_ROLE_DELETE" { - missing!(value, Event::GuildRoleDelete(GuildRoleDeleteEvent { + Ok(Event::GuildRoleDelete(GuildRoleDeleteEvent { guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), role_id: try!(remove(&mut value, "role_id").and_then(RoleId::decode)), })) } else if kind == "GUILD_ROLE_UPDATE" { - missing!(value, Event::GuildRoleUpdate(GuildRoleUpdateEvent { + Ok(Event::GuildRoleUpdate(GuildRoleUpdateEvent { guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), role: try!(remove(&mut value, "role").and_then(Role::decode)), })) } else if kind == "GUILD_SYNC" { - missing!(value, Event::GuildSync(GuildSyncEvent { + Ok(Event::GuildSync(GuildSyncEvent { guild_id: try!(remove(&mut value, "id").and_then(GuildId::decode)), large: req!(try!(remove(&mut value, "large")).as_bool()), members: try!(remove(&mut value, "members").and_then(decode_members)), @@ -628,7 +628,7 @@ impl Event { guild: try!(PartialGuild::decode(Value::Object(value))), })) } else if kind == "MESSAGE_ACK" { - missing!(value, Event::MessageAck(MessageAckEvent { + Ok(Event::MessageAck(MessageAckEvent { channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), message_id: try!(opt(&mut value, "message_id", MessageId::decode)), })) @@ -637,12 +637,12 @@ impl Event { message: try!(Message::decode(Value::Object(value))), })) } else if kind == "MESSAGE_DELETE" { - missing!(value, Event::MessageDelete(MessageDeleteEvent { + Ok(Event::MessageDelete(MessageDeleteEvent { channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), message_id: try!(remove(&mut value, "id").and_then(MessageId::decode)), })) } else if kind == "MESSAGE_DELETE_BULK" { - missing!(value, Event::MessageDeleteBulk(MessageDeleteBulkEvent { + Ok(Event::MessageDeleteBulk(MessageDeleteBulkEvent { channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), ids: try!(decode_array(try!(remove(&mut value, "ids")), MessageId::decode)), })) @@ -650,7 +650,7 @@ impl Event { Ok(Event::ReactionAdd(ReactionAddEvent { reaction: try!(Reaction::decode(Value::Object(value))) })) - } else if kind == "MESSAG_REACTION_REMOVE" { + } else if kind == "MESSAGE_REACTION_REMOVE" { Ok(Event::ReactionRemove(ReactionRemoveEvent { reaction: try!(Reaction::decode(Value::Object(value))) })) @@ -660,7 +660,7 @@ impl Event { message_id: try!(remove(&mut value, "message_id").and_then(MessageId::decode)), })) } else if kind == "MESSAGE_UPDATE" { - missing!(value, Event::MessageUpdate(MessageUpdateEvent { + Ok(Event::MessageUpdate(MessageUpdateEvent { id: try!(remove(&mut value, "id").and_then(MessageId::decode)), channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), kind: try!(opt(&mut value, "type", MessageType::decode)), @@ -691,7 +691,7 @@ impl Event { relationship: try!(Relationship::decode(Value::Object(value))), })) } else if kind == "RELATIONSHIP_REMOVE" { - missing!(value, Event::RelationshipRemove(RelationshipRemoveEvent { + Ok(Event::RelationshipRemove(RelationshipRemoveEvent { kind: try!(remove(&mut value, "type").and_then(RelationshipType::decode)), user_id: try!(remove(&mut value, "id").and_then(UserId::decode)), })) @@ -700,12 +700,12 @@ impl Event { ready: try!(Ready::decode(Value::Object(value))), })) } else if kind == "RESUMED" { - missing!(value, Event::Resumed(ResumedEvent { + Ok(Event::Resumed(ResumedEvent { heartbeat_interval: req!(try!(remove(&mut value, "heartbeat_interval")).as_u64()), trace: try!(remove(&mut value, "_trace").and_then(|v| decode_array(v, |v| Ok(into_string(v).ok())))), })) } else if kind == "TYPING_START" { - missing!(value, Event::TypingStart(TypingStartEvent { + Ok(Event::TypingStart(TypingStartEvent { channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), timestamp: req!(try!(remove(&mut value, "timestamp")).as_u64()), user_id: try!(remove(&mut value, "user_id").and_then(UserId::decode)), @@ -715,12 +715,12 @@ impl Event { settings: try!(UserGuildSettings::decode(Value::Object(value))), })) } else if kind == "USER_NOTE_UPDATE" { - missing!(value, Event::UserNoteUpdate(UserNoteUpdateEvent { + Ok(Event::UserNoteUpdate(UserNoteUpdateEvent { note: try!(remove(&mut value, "note").and_then(into_string)), user_id: try!(remove(&mut value, "id").and_then(UserId::decode)), })) } else if kind == "USER_SETTINGS_UPDATE" { - missing!(value, Event::UserSettingsUpdate(UserSettingsUpdateEvent { + Ok(Event::UserSettingsUpdate(UserSettingsUpdateEvent { enable_tts_command: remove(&mut value, "enable_tts_command").ok().and_then(|v| v.as_bool()), inline_attachment_media: remove(&mut value, "inline_attachment_media").ok().and_then(|v| v.as_bool()), inline_embed_media: remove(&mut value, "inline_embed_media").ok().and_then(|v| v.as_bool()), @@ -737,7 +737,7 @@ impl Event { current_user: try!(CurrentUser::decode(Value::Object(value))), })) } else if kind == "VOICE_SERVER_UPDATE" { - missing!(value, Event::VoiceServerUpdate(VoiceServerUpdateEvent { + Ok(Event::VoiceServerUpdate(VoiceServerUpdateEvent { guild_id: try!(opt(&mut value, "guild_id", GuildId::decode)), channel_id: try!(opt(&mut value, "channel_id", ChannelId::decode)), endpoint: try!(opt(&mut value, "endpoint", into_string)), @@ -761,3 +761,90 @@ impl Event { } } } + +#[derive(Clone, Copy, Debug)] +pub struct VoiceHeartbeat { + pub heartbeat_interval: u64, +} + +#[derive(Clone, Debug)] +pub struct VoiceHello { + pub heartbeat_interval: u64, + pub ip: String, + pub modes: Vec<String>, + pub port: u16, + pub ssrc: u32, +} + +#[derive(Clone, Debug)] +pub struct VoiceReady { + pub mode: String, + pub secret_key: Vec<u8>, +} + +#[derive(Clone, Copy, Debug)] +pub struct VoiceSpeaking { + pub speaking: bool, + pub ssrc: u32, + pub user_id: UserId, +} + +#[derive(Clone, Debug)] +pub enum VoiceEvent { + Heartbeat(VoiceHeartbeat), + Hello(VoiceHello), + Ready(VoiceReady), + Speaking(VoiceSpeaking), + KeepAlive, + Unknown(VoiceOpCode, Value) +} + +impl VoiceEvent { + pub fn decode(value: Value) -> Result<VoiceEvent> { + let mut value = try!(into_map(value)); + let op = req!(try!(remove(&mut value, "op")).as_u64()); + let mut map = try!(remove(&mut value, "d").and_then(into_map)); + + let opcode = try!(VoiceOpCode::from_num(op) + .ok_or(Error::Client(ClientError::InvalidOpCode))); + + match opcode { + VoiceOpCode::Heartbeat => { + Ok(VoiceEvent::Heartbeat(VoiceHeartbeat { + heartbeat_interval: req!(try!(remove(&mut map, "heartbeat_interval")).as_u64()), + })) + }, + VoiceOpCode::Hello => { + Ok(VoiceEvent::Hello(VoiceHello { + heartbeat_interval: req!(try!(remove(&mut map, "heartbeat_interval")) + .as_u64()), + ip: try!(remove(&mut map, "ip").and_then(into_string)), + modes: try!(decode_array(try!(remove(&mut map, "modes")), + into_string)), + port: req!(try!(remove(&mut map, "port")) + .as_u64()) as u16, + ssrc: req!(try!(remove(&mut map, "ssrc")) + .as_u64()) as u32, + })) + }, + VoiceOpCode::KeepAlive => Ok(VoiceEvent::KeepAlive), + VoiceOpCode::SessionDescription => { + Ok(VoiceEvent::Ready(VoiceReady { + mode: try!(remove(&mut map, "mode") + .and_then(into_string)), + secret_key: try!(decode_array(try!(remove(&mut map, "secret_key")), + |v| Ok(req!(v.as_u64()) as u8) + )), + })) + }, + VoiceOpCode::Speaking => { + Ok(VoiceEvent::Speaking(VoiceSpeaking { + speaking: req!(try!(remove(&mut map, "speaking")).as_bool()), + ssrc: req!(try!(remove(&mut map, "ssrc")).as_u64()) as u32, + user_id: try!(remove(&mut map, "user_id").and_then(UserId::decode)), + })) + } + other => Ok(VoiceEvent::Unknown(other, Value::Object(map))), + } + } +} diff --git a/src/model/gateway.rs b/src/model/gateway.rs index 30ed695..93c779b 100644 --- a/src/model/gateway.rs +++ b/src/model/gateway.rs @@ -36,7 +36,7 @@ impl Game { return Ok(None); } - missing!(map, Some(Game { + Ok(Some(Game { name: name, kind: try!(opt(&mut map, "type", GameType::decode)).unwrap_or(GameType::Playing), url: try!(opt(&mut map, "url", into_string)), @@ -62,7 +62,7 @@ impl Presence { Some(v) => try!(Game::decode(v)), }; - missing!(value, Presence { + Ok(Presence { user_id: user_id, status: try!(remove(&mut value, "status").and_then(OnlineStatus::decode_str)), last_modified: try!(opt(&mut value, "last_modified", |v| Ok(req!(v.as_u64())))), diff --git a/src/model/guild.rs b/src/model/guild.rs index 4869e86..9408fae 100644 --- a/src/model/guild.rs +++ b/src/model/guild.rs @@ -10,7 +10,6 @@ use super::utils::{ into_string, opt, remove, - warn_field }; use super::*; use ::internal::prelude::*; @@ -122,7 +121,7 @@ impl GuildInfo { /// Returns the formatted URL of the guild's icon, if the guild has an icon. pub fn icon_url(&self) -> Option<String> { self.icon.as_ref().map(|icon| - format!(cdn_concat!("/icons/{}/{}.jpg"), self.id, icon)) + format!(cdn!("/icons/{}/{}.jpg"), self.id, icon)) } } @@ -149,7 +148,7 @@ impl PartialGuild { /// Returns a formatted URL of the guild's icon, if the guild has an icon. pub fn icon_url(&self) -> Option<String> { self.icon.as_ref().map(|icon| - format!(cdn_concat!("/icons/{}/{}.jpg"), self.id, icon)) + format!(cdn!("/icons/{}/{}.jpg"), self.id, icon)) } /// Retrieves the guild's webhooks. @@ -173,7 +172,6 @@ impl Guild { }; let perms = self.permissions_for(ChannelId(self.id.0), member.user.id); - permissions.remove(perms); Ok(permissions.is_empty()) @@ -321,10 +319,8 @@ impl Guild { } }} - let role = { - try!(rest::create_role(self.id.0)) - }; - let map = f(EditRole::default()).0.build(); + let role = try!(rest::create_role(self.id.0)); + let map = f(EditRole::new(&role)).0.build(); rest::edit_role(self.id.0, role.id.0, map) } @@ -348,7 +344,7 @@ impl Guild { public_channels }; - missing!(map, Guild { + Ok(Guild { afk_channel_id: try!(opt(&mut map, "afk_channel_id", ChannelId::decode)), afk_timeout: req!(try!(remove(&mut map, "afk_timeout")).as_u64()), channels: public_channels, @@ -557,7 +553,7 @@ impl Guild { /// Returns the formatted URL of the guild's icon, if one exists. pub fn icon_url(&self) -> Option<String> { self.icon.as_ref().map(|icon| - format!(cdn_concat!("/icons/{}/{}.jpg"), self.id, icon)) + format!(cdn!("/icons/{}/{}.jpg"), self.id, icon)) } /// Checks if the guild is 'large'. A guild is considered large if it has diff --git a/src/model/user.rs b/src/model/user.rs index 4201bd2..d7ea416 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -1,5 +1,5 @@ use std::fmt; -use super::utils::{into_map, into_string, remove, warn_field}; +use super::utils::{into_map, into_string, remove}; use super::{ CurrentUser, FriendSourceFlags, @@ -29,7 +29,7 @@ impl CurrentUser { /// Returns the formatted URL of the user's icon, if one exists. pub fn avatar_url(&self) -> Option<String> { self.avatar.as_ref().map(|av| - format!(cdn_concat!("/avatars/{}/{}.jpg"), self.id, av)) + format!(cdn!("/avatars/{}/{}.jpg"), self.id, av)) } } @@ -37,7 +37,7 @@ impl User { /// Returns the formatted URL of the user's icon, if one exists. pub fn avatar_url(&self) -> Option<String> { self.avatar.as_ref().map(|av| - format!(cdn_concat!("/avatars/{}/{}.jpg"), self.id, av)) + format!(cdn!("/avatars/{}/{}.jpg"), self.id, av)) } /// Retrieves the time that this user was created at. @@ -166,7 +166,7 @@ impl UserSettings { return Ok(None); } - missing!(map, UserSettings { + Ok(UserSettings { convert_emoticons: req!(try!(remove(&mut map, "convert_emoticons")).as_bool()), enable_tts_command: req!(try!(remove(&mut map, "enable_tts_command")).as_bool()), friend_source_flags: try!(remove(&mut map, "friend_source_flags").and_then(FriendSourceFlags::decode)), diff --git a/src/model/utils.rs b/src/model/utils.rs index 0611a9f..9b4f6e4 100644 --- a/src/model/utils.rs +++ b/src/model/utils.rs @@ -25,19 +25,6 @@ use ::client::CACHE; use ::ext::cache::ChannelRef; #[macro_escape] -macro_rules! missing { - (@ $name:expr, $json:ident, $value:expr) => { - (Ok($value), warn_field($name, $json)).0 - }; - ($json:ident, $ty:ident $(::$ext:ident)* ( $($value:expr),*$(,)* ) ) => { - (Ok($ty$(::$ext)* ( $($value),* )), warn_field(stringify!($ty$(::$ext)*), $json)).0 - }; - ($json:ident, $ty:ident $(::$ext:ident)* { $($name:ident: $value:expr),*$(,)* } ) => { - (Ok($ty$(::$ext)* { $($name: $value),* }), warn_field(stringify!($ty$(::$ext)*), $json)).0 - }; -} - -#[macro_escape] macro_rules! req { ($opt:expr) => { try!($opt.ok_or(Error::Decode(concat!("Type mismatch in model:", @@ -253,7 +240,7 @@ pub fn opt<T, F: FnOnce(Value) -> Result<T>>(map: &mut BTreeMap<String, Value>, } } -pub fn parse_discriminator(value: Value) -> Result<u16> { +pub fn decode_discriminator(value: Value) -> Result<u16> { match value { Value::I64(v) => Ok(v as u16), Value::U64(v) => Ok(v as u16), @@ -303,9 +290,3 @@ pub fn user_has_perms(channel_id: ChannelId, Ok(permissions.is_empty()) } - -pub fn warn_field(name: &str, map: BTreeMap<String, Value>) { - if !map.is_empty() { - debug!("Unhandled keys: {} has {:?}", name, Value::Object(map)) - } -} diff --git a/src/model/voice.rs b/src/model/voice.rs index 61035c6..8b13789 100644 --- a/src/model/voice.rs +++ b/src/model/voice.rs @@ -1,92 +1 @@ -use super::utils::{into_map, into_string, remove, warn_field}; -use super::UserId; -use ::constants::VoiceOpCode; -use ::internal::prelude::*; -use ::utils::decode_array; -#[derive(Clone, Copy, Debug)] -pub struct VoiceHeartbeat { - pub heartbeat_interval: u64, -} - -#[derive(Clone, Debug)] -pub struct VoiceHello { - pub heartbeat_interval: u64, - pub ip: String, - pub modes: Vec<String>, - pub port: u16, - pub ssrc: u32, -} - -#[derive(Clone, Debug)] -pub struct VoiceReady { - pub mode: String, - pub secret_key: Vec<u8>, -} - -#[derive(Clone, Copy, Debug)] -pub struct VoiceSpeaking { - pub speaking: bool, - pub ssrc: u32, - pub user_id: UserId, -} - -#[derive(Clone, Debug)] -pub enum VoiceEvent { - Heartbeat(VoiceHeartbeat), - Hello(VoiceHello), - Ready(VoiceReady), - Speaking(VoiceSpeaking), - KeepAlive, - Unknown(VoiceOpCode, Value) -} - -impl VoiceEvent { - pub fn decode(value: Value) -> Result<VoiceEvent> { - let mut value = try!(into_map(value)); - let op = req!(try!(remove(&mut value, "op")).as_u64()); - let mut map = try!(remove(&mut value, "d").and_then(into_map)); - - let opcode = try!(VoiceOpCode::from_num(op) - .ok_or(Error::Client(ClientError::InvalidOpCode))); - - match opcode { - VoiceOpCode::Heartbeat => { - missing!(map, VoiceEvent::Heartbeat(VoiceHeartbeat { - heartbeat_interval: req!(try!(remove(&mut map, "heartbeat_interval")).as_u64()), - })) - }, - VoiceOpCode::Hello => { - missing!(map, VoiceEvent::Hello(VoiceHello { - heartbeat_interval: req!(try!(remove(&mut map, "heartbeat_interval")) - .as_u64()), - ip: try!(remove(&mut map, "ip").and_then(into_string)), - modes: try!(decode_array(try!(remove(&mut map, "modes")), - into_string)), - port: req!(try!(remove(&mut map, "port")) - .as_u64()) as u16, - ssrc: req!(try!(remove(&mut map, "ssrc")) - .as_u64()) as u32, - })) - }, - VoiceOpCode::KeepAlive => Ok(VoiceEvent::KeepAlive), - VoiceOpCode::SessionDescription => { - missing!(map, VoiceEvent::Ready(VoiceReady { - mode: try!(remove(&mut map, "mode") - .and_then(into_string)), - secret_key: try!(decode_array(try!(remove(&mut map, "secret_key")), - |v| Ok(req!(v.as_u64()) as u8) - )), - })) - }, - VoiceOpCode::Speaking => { - missing!(map, VoiceEvent::Speaking(VoiceSpeaking { - speaking: req!(try!(remove(&mut map, "speaking")).as_bool()), - ssrc: req!(try!(remove(&mut map, "ssrc")).as_u64()) as u32, - user_id: try!(remove(&mut map, "user_id").and_then(UserId::decode)), - })) - } - other => Ok(VoiceEvent::Unknown(other, Value::Object(map))), - } - } -} diff --git a/src/utils/builder/create_embed.rs b/src/utils/builder/create_embed.rs index 0b0322c..369784f 100644 --- a/src/utils/builder/create_embed.rs +++ b/src/utils/builder/create_embed.rs @@ -349,39 +349,3 @@ impl Default for CreateEmbedThumbnail { CreateEmbedThumbnail(ObjectBuilder::new()) } } - -/// A builder to create a fake [`Embed`] object's video, for use with the -/// [`CreateEmbed::video`] method. -/// -/// Requires that you specify a [`url`]. -/// -/// [`Embed`]: ../../model/struct.Embed.html -/// [`CreateEmbed::video`]: struct.CreateEmbed.html#method.video -/// [`url`]: #method.url -pub struct CreateEmbedVideo(pub ObjectBuilder); - -impl CreateEmbedVideo { - /// Set the height of the video, in pixels. - pub fn height(self, height: u64) -> Self { - CreateEmbedVideo(self.0.insert("height", height)) - } - - /// Set the source URL of the video. - /// - /// _Must_ be specified. - pub fn url(self, url: &str) -> Self { - CreateEmbedVideo(self.0.insert("url", url)) - } - - /// Set the width of the video, in pixels. - pub fn width(self, width: &str) -> Self { - CreateEmbedVideo(self.0.insert("width", width)) - } -} - -impl Default for CreateEmbedVideo { - /// Creates a builder with no default values. - fn default() -> CreateEmbedVideo { - CreateEmbedVideo(ObjectBuilder::new()) - } -} diff --git a/src/utils/builder/mod.rs b/src/utils/builder/mod.rs index bc8a20d..497d309 100644 --- a/src/utils/builder/mod.rs +++ b/src/utils/builder/mod.rs @@ -23,7 +23,6 @@ pub use self::create_embed::{ CreateEmbedField, CreateEmbedImage, CreateEmbedThumbnail, - CreateEmbedVideo, }; pub use self::create_invite::CreateInvite; pub use self::create_message::CreateMessage; diff --git a/src/utils/macros.rs b/src/utils/macros.rs index d9026af..e8f74dd 100644 --- a/src/utils/macros.rs +++ b/src/utils/macros.rs @@ -19,11 +19,11 @@ macro_rules! request { ($route:expr, $method:ident, $url:expr) => {{ let client = HyperClient::new(); try!(request($route, || client - .$method(api_concat!($url)))) + .$method(api!($url)))) }}; } -macro_rules! cdn_concat { +macro_rules! cdn { ($e:expr) => { concat!("https://cdn.discordapp.com", $e) } @@ -37,12 +37,7 @@ macro_rules! api { }; } -macro_rules! api_concat { - ($e:expr) => { - concat!("https://discordapp.com/api/v6", $e) - } -} -macro_rules! status_concat { +macro_rules! status { ($e:expr) => { concat!("https://status.discordapp.com/api/v2", $e) } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 0c154dd..aa97f4a 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -21,60 +21,6 @@ use ::internal::prelude::*; pub use self::message_builder::MessageBuilder; -macro_rules! cdn_concat { - ($e:expr) => { - concat!("https://cdn.discordapp.com", $e) - } -} -macro_rules! api { - ($e:expr) => { - concat!("https://discordapp.com/api/v6", $e) - }; - ($e:expr, $($rest:tt)*) => { - format!(api!($e), $($rest)*) - }; -} - -macro_rules! api_concat { - ($e:expr) => { - concat!("https://discordapp.com/api/v6", $e) - } -} -macro_rules! status_concat { - ($e:expr) => { - concat!("https://status.discordapp.com/api/v2", $e) - } -} - -macro_rules! map_nums { - ($item:ident; $($entry:ident $value:expr,)*) => { - impl $item { - #[allow(dead_code)] - pub fn num(&self) -> u64 { - match *self { - $($item::$entry => $value,)* - } - } - - #[allow(dead_code)] - pub fn from_num(num: u64) -> Option<Self> { - match num { - $($value => Some($item::$entry),)* - _ => None, - } - } - - #[allow(dead_code)] - fn decode(value: Value) -> Result<Self> { - value.as_u64().and_then(Self::from_num).ok_or(Error::Decode( - concat!("Expected valid ", stringify!($item)), - value - )) - } - } - } -} - #[doc(hidden)] pub fn decode_array<T, F: Fn(Value) -> Result<T>>(value: Value, f: F) -> Result<Vec<T>> { into_array(value).and_then(|x| x.into_iter().map(f).collect()) |