diff --git a/src/http/mod.rs b/src/http/mod.rs index 05b198aa7a0..9151cfdce05 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -33,12 +33,13 @@ pub use hyper::status::{StatusClass, StatusCode}; use constants; use hyper::{ client::{ + Body as HyperBody, Client as HyperClient, - Request, RequestBuilder, + Request as HyperRequest, Response as HyperResponse }, - header::ContentType, + header::{ContentType, Headers}, method::Method, mime::{Mime, SubLevel, TopLevel}, net::HttpsConnector, @@ -73,6 +74,14 @@ lazy_static! { }; } +struct Request { + body: Option>, + headers: Option, + method: Method, + route: Route, + url: String, +} + /// An method used for ratelimiting special routes. /// /// This is needed because `hyper`'s `Method` enum does not derive Copy. @@ -130,16 +139,13 @@ pub fn set_token(token: &str) { TOKEN.lock().clone_from(&token.to_string()); } /// [`Group::add_recipient`]: ../model/channel/struct.Group.html#method.add_recipient /// [`User`]: ../model/user/struct.User.html pub fn add_group_recipient(group_id: u64, user_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::None, - put, - "/channels/{}/recipients/{}", - group_id, - user_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Put, + route: Route::None, + url: api!("/channels/{}/recipients/{}", group_id, user_id), + })?) } /// Adds a single [`Role`] to a [`Member`] in a [`Guild`]. @@ -152,17 +158,13 @@ pub fn add_group_recipient(group_id: u64, user_id: u64) -> Result<()> { /// [`Role`]: ../model/guild/struct.Role.html /// [Manage Roles]: ../model/permissions/constant.MANAGE_ROLES.html pub fn add_member_role(guild_id: u64, user_id: u64, role_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::GuildsIdMembersIdRolesId(guild_id), - put, - "/guilds/{}/members/{}/roles/{}", - guild_id, - user_id, - role_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Put, + route: Route::GuildsIdMembersIdRolesId(guild_id), + url: api!("/guilds/{}/members/{}/roles/{}", guild_id, user_id, role_id), + })?) } /// Bans a [`User`] from a [`Guild`], removing their messages sent in the last @@ -177,18 +179,19 @@ pub fn add_member_role(guild_id: u64, user_id: u64, role_id: u64) -> Result<()> /// [`User`]: ../model/user/struct.User.html /// [Ban Members]: ../model/permissions/constant.BAN_MEMBERS.html pub fn ban_user(guild_id: u64, user_id: u64, delete_message_days: u8, reason: &str) -> Result<()> { - verify( - 204, - request!( - Route::GuildsIdBansUserId(guild_id), - put, + verify(204, request(Request { + body: None, + headers: None, + method: Method::Put, + route: Route::GuildsIdBansUserId(guild_id), + url: api!( "/guilds/{}/bans/{}?delete_message_days={}&reason={}", guild_id, user_id, delete_message_days, - reason + reason, ), - ) + })?) } /// Ban zeyla from a [`Guild`], removing her messages sent in the last X number @@ -244,15 +247,13 @@ pub fn ban_servermoms(guild_id: u64, delete_message_days: u8, reason: &str) -> R /// /// [`Channel`]: ../model/channel/enum.Channel.html pub fn broadcast_typing(channel_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::ChannelsIdTyping(channel_id), - post, - "/channels/{}/typing", - channel_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Post, + route: Route::ChannelsIdTyping(channel_id), + url: api!("/channels/{}/typing", channel_id), + })?) } /// Creates a [`GuildChannel`] in the [`Guild`] given its Id. @@ -266,13 +267,13 @@ pub fn broadcast_typing(channel_id: u64) -> Result<()> { /// [docs]: https://discordapp.com/developers/docs/resources/guild#create-guild-channel /// [Manage Channels]: ../model/permissions/constant.MANAGE_CHANNELS.html pub fn create_channel(guild_id: u64, map: &Value) -> Result { - let body = map.to_string(); - let response = request!( - Route::GuildsIdChannels(guild_id), - post(body), - "/guilds/{}/channels", - guild_id - ); + let response = request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Post, + route: Route::GuildsIdChannels(guild_id), + url: api!("/guilds/{}/channels", guild_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -289,13 +290,13 @@ pub fn create_channel(guild_id: u64, map: &Value) -> Result { /// [`Guild`]: ../model/guild/struct.Guild.html /// [Manage Emojis]: ../model/permissions/constant.MANAGE_EMOJIS.html pub fn create_emoji(guild_id: u64, map: &Value) -> Result { - let body = map.to_string(); - let response = request!( - Route::GuildsIdEmojis(guild_id), - post(body), - "/guilds/{}/emojis", - guild_id - ); + let response = request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Post, + route: Route::GuildsIdEmojis(guild_id), + url: api!("/guilds/{}/emojis", guild_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -338,8 +339,13 @@ pub fn create_emoji(guild_id: u64, map: &Value) -> Result { /// https://discordapp.com/developers/docs/resources/guild#create-guild /// [whitelist]: https://discordapp.com/developers/docs/resources/guild#create-guild pub fn create_guild(map: &Value) -> Result { - let body = map.to_string(); - let response = request!(Route::Guilds, post(body), "/guilds"); + let response = request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Post, + route: Route::Guilds, + url: api!("/guilds").to_owned(), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -356,18 +362,13 @@ pub fn create_guild(map: &Value) -> Result { /// [Manage Guild]: ../model/permissions/constant.MANAGE_GUILD.html /// [docs]: https://discordapp.com/developers/docs/resources/guild#create-guild-integration pub fn create_guild_integration(guild_id: u64, integration_id: u64, map: &Value) -> Result<()> { - let body = map.to_string(); - - verify( - 204, - request!( - Route::GuildsIdIntegrations(guild_id), - post(body), - "/guilds/{}/integrations/{}", - guild_id, - integration_id - ), - ) + verify(204, request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Post, + route: Route::GuildsIdIntegrations(guild_id), + url: api!("/guilds/{}/integrations/{}", guild_id, integration_id), + })?) } /// Creates a [`RichInvite`] for the given [channel][`GuildChannel`]. @@ -383,13 +384,15 @@ pub fn create_guild_integration(guild_id: u64, integration_id: u64, map: &Value) /// [Create Invite]: ../model/permissions/constant.CREATE_INVITE.html /// [docs]: https://discordapp.com/developers/docs/resources/channel#create-channel-invite pub fn create_invite(channel_id: u64, map: &JsonMap) -> Result { - let body = serde_json::to_string(map)?; - let response = request!( - Route::ChannelsIdInvites(channel_id), - post(body), - "/channels/{}/invites", - channel_id - ); + let body = serde_json::to_vec(map)?; + + let response = request(Request { + body: Some(body), + headers: None, + method: Method::Post, + route: Route::ChannelsIdInvites(channel_id), + url: api!("/channels/{}/invites", channel_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -397,24 +400,24 @@ pub fn create_invite(channel_id: u64, map: &JsonMap) -> Result { /// Creates a permission override for a member or a role in a channel. pub fn create_permission(channel_id: u64, target_id: u64, map: &Value) -> Result<()> { - let body = map.to_string(); - - verify( - 204, - request!( - Route::ChannelsIdPermissionsOverwriteId(channel_id), - put(body), - "/channels/{}/permissions/{}", - channel_id, - target_id - ), - ) + verify(204, request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Put, + route: Route::ChannelsIdPermissionsOverwriteId(channel_id), + url: api!("/channels/{}/permissions/{}", channel_id, target_id), + })?) } /// Creates a private channel with a user. pub fn create_private_channel(map: &Value) -> Result { - let body = map.to_string(); - let response = request!(Route::UsersMeChannels, post(body), "/users/@me/channels"); + let response = request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Post, + route: Route::UsersMeChannels, + url: api!("/users/@me/channels").to_owned(), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -425,28 +428,31 @@ pub fn create_reaction(channel_id: u64, message_id: u64, reaction_type: &ReactionType) -> Result<()> { - verify( - 204, - request!( - Route::ChannelsIdMessagesIdReactionsUserIdType(channel_id), - put, + verify(204, request(Request { + body: None, + headers: None, + method: Method::Put, + route: Route::ChannelsIdMessagesIdReactionsUserIdType(channel_id), + url: api!( "/channels/{}/messages/{}/reactions/{}/@me", channel_id, message_id, - reaction_type.as_data() + reaction_type.as_data(), ), - ) + })?) } /// Creates a role. pub fn create_role(guild_id: u64, map: &JsonMap) -> Result { - let body = serde_json::to_string(map)?; - let response = request!( - Route::GuildsIdRoles(guild_id), - post(body), - "/guilds/{}/roles", - guild_id - ); + let body = serde_json::to_vec(map)?; + + let response = request(Request { + body: Some(body), + headers: None, + method: Method::Post, + route: Route::GuildsIdRoles(guild_id), + url: api!("/guilds/{}/roles", guild_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -483,13 +489,13 @@ pub fn create_role(guild_id: u64, map: &JsonMap) -> Result { /// /// [`GuildChannel`]: ../model/channel/struct.GuildChannel.html pub fn create_webhook(channel_id: u64, map: &Value) -> Result { - let body = map.to_string(); - let response = request!( - Route::ChannelsIdWebhooks(channel_id), - post(body), - "/channels/{}/webhooks", - channel_id - ); + let response = request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Post, + route: Route::ChannelsIdWebhooks(channel_id), + url: api!("/channels/{}/webhooks", channel_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -497,12 +503,13 @@ pub fn create_webhook(channel_id: u64, map: &Value) -> Result { /// Deletes a private channel or a channel in a guild. pub fn delete_channel(channel_id: u64) -> Result { - let response = request!( - Route::ChannelsId(channel_id), - delete, - "/channels/{}", - channel_id - ); + let response = request(Request { + body: None, + headers: None, + route: Route::ChannelsId(channel_id), + method: Method::Delete, + url: api!("/channels/{}", channel_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -510,21 +517,24 @@ pub fn delete_channel(channel_id: u64) -> Result { /// Deletes an emoji from a server. pub fn delete_emoji(guild_id: u64, emoji_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::GuildsIdEmojisId(guild_id), - delete, - "/guilds/{}/emojis/{}", - guild_id, - emoji_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::GuildsIdEmojisId(guild_id), + url: api!("/guilds/{}/emojis/{}", guild_id, emoji_id), + })?) } /// Deletes a guild, only if connected account owns it. pub fn delete_guild(guild_id: u64) -> Result { - let response = request!(Route::GuildsId(guild_id), delete, "/guilds/{}", guild_id); + let response = request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::GuildsId(guild_id), + url: api!("/guilds/{}", guild_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -532,21 +542,24 @@ pub fn delete_guild(guild_id: u64) -> Result { /// Remvoes an integration from a guild. pub fn delete_guild_integration(guild_id: u64, integration_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::GuildsIdIntegrationsId(guild_id), - delete, - "/guilds/{}/integrations/{}", - guild_id, - integration_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::GuildsIdIntegrationsId(guild_id), + url: api!("/guilds/{}/integrations/{}", guild_id, integration_id), + })?) } /// Deletes an invite by code. pub fn delete_invite(code: &str) -> Result { - let response = request!(Route::InvitesCode, delete, "/invites/{}", code); + let response = request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::InvitesCode, + url: api!("/invites/{}", code), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -555,31 +568,24 @@ pub fn delete_invite(code: &str) -> Result { /// Deletes a message if created by us or we have /// specific permissions. pub fn delete_message(channel_id: u64, message_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::ChannelsIdMessagesId(LightMethod::Delete, channel_id), - delete, - "/channels/{}/messages/{}", - channel_id, - message_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::ChannelsIdMessagesId(LightMethod::Delete, channel_id), + url: api!("/channels/{}/messages/{}", channel_id, message_id), + })?) } /// Deletes a bunch of messages, only works for bots. pub fn delete_messages(channel_id: u64, map: &Value) -> Result<()> { - let body = map.to_string(); - - verify( - 204, - request!( - Route::ChannelsIdMessagesBulkDelete(channel_id), - post(body), - "/channels/{}/messages/bulk-delete", - channel_id - ), - ) + verify(204, request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Delete, + route: Route::ChannelsIdMessagesBulkDelete(channel_id), + url: api!("/channels/{}/messages/bulk-delete", channel_id), + })?) } /// Deletes all of the [`Reaction`]s associated with a [`Message`]. @@ -600,30 +606,24 @@ pub fn delete_messages(channel_id: u64, map: &Value) -> Result<()> { /// [`Message`]: ../model/channel/struct.Message.html /// [`Reaction`]: ../model/channel/struct.Reaction.html pub fn delete_message_reactions(channel_id: u64, message_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::ChannelsIdMessagesIdReactions(channel_id), - delete, - "/channels/{}/messages/{}/reactions", - channel_id, - message_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::ChannelsIdMessagesIdReactions(channel_id), + url: api!("/channels/{}/messages/{}/reactions", channel_id, message_id), + })?) } /// Deletes a permission override from a role or a member in a channel. pub fn delete_permission(channel_id: u64, target_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::ChannelsIdPermissionsOverwriteId(channel_id), - delete, - "/channels/{}/permissions/{}", - channel_id, - target_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::ChannelsIdPermissionsOverwriteId(channel_id), + url: api!("/channels/{}/permissions/{}", channel_id, target_id), + })?) } /// Deletes a reaction from a message if owned by us or @@ -637,32 +637,30 @@ pub fn delete_reaction(channel_id: u64, .map(|uid| uid.to_string()) .unwrap_or_else(|| "@me".to_string()); - verify( - 204, - request!( - Route::ChannelsIdMessagesIdReactionsUserIdType(channel_id), - delete, + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::ChannelsIdMessagesIdReactionsUserIdType(channel_id), + url: api!( "/channels/{}/messages/{}/reactions/{}/{}", channel_id, message_id, reaction_type.as_data(), - user + user, ), - ) + })?) } /// Deletes a role from a server. Can't remove the default everyone role. pub fn delete_role(guild_id: u64, role_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::GuildsIdRolesId(guild_id), - delete, - "/guilds/{}/roles/{}", - guild_id, - role_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::GuildsIdRolesId(guild_id), + url: api!("/guilds/{}/roles/{}", guild_id, role_id), + })?) } /// Deletes a [`Webhook`] given its Id. @@ -688,15 +686,13 @@ pub fn delete_role(guild_id: u64, role_id: u64) -> Result<()> { /// [`Webhook`]: ../model/webhook/struct.Webhook.html /// [`delete_webhook_with_token`]: fn.delete_webhook_with_token.html pub fn delete_webhook(webhook_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::WebhooksId(webhook_id), - delete, - "/webhooks/{}", - webhook_id, - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::WebhooksId(webhook_id), + url: api!("/webhooks/{}", webhook_id), + })?) } /// Deletes a [`Webhook`] given its Id and unique token. @@ -718,24 +714,26 @@ pub fn delete_webhook(webhook_id: u64) -> Result<()> { /// /// [`Webhook`]: ../model/webhook/struct.Webhook.html pub fn delete_webhook_with_token(webhook_id: u64, token: &str) -> Result<()> { - verify( - 204, - retry(|| { - CLIENT - .delete(&format!(api!("/webhooks/{}/{}"), webhook_id, token)) - }).map_err(Error::Hyper)?, - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::None, + url: api!("/webhooks/{}/{}", webhook_id, token), + })?) } /// Changes channel information. pub fn edit_channel(channel_id: u64, map: &JsonMap) -> Result { - let body = serde_json::to_string(map)?; - let response = request!( - Route::ChannelsId(channel_id), - patch(body), - "/channels/{}", - channel_id - ); + let body = serde_json::to_vec(map)?; + + let response = request(Request { + body: Some(body), + headers: None, + method: Method::Patch, + route: Route::ChannelsId(channel_id), + url: api!("/channels/{}", channel_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -743,14 +741,13 @@ pub fn edit_channel(channel_id: u64, map: &JsonMap) -> Result { /// Changes emoji information. pub fn edit_emoji(guild_id: u64, emoji_id: u64, map: &Value) -> Result { - let body = map.to_string(); - let response = request!( - Route::GuildsIdEmojisId(guild_id), - patch(body), - "/guilds/{}/emojis/{}", - guild_id, - emoji_id - ); + let response = request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Patch, + route: Route::GuildsIdEmojisId(guild_id), + url: api!("/guilds/{}/emojis/{}", guild_id, emoji_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -758,13 +755,15 @@ pub fn edit_emoji(guild_id: u64, emoji_id: u64, map: &Value) -> Result { /// Changes guild information. pub fn edit_guild(guild_id: u64, map: &JsonMap) -> Result { - let body = serde_json::to_string(map)?; - let response = request!( - Route::GuildsId(guild_id), - patch(body), - "/guilds/{}", - guild_id - ); + let body = serde_json::to_vec(map)?; + + let response = request(Request { + body: Some(body), + headers: None, + method: Method::Patch, + route: Route::GuildsId(guild_id), + url: api!("/guilds/{}", guild_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -773,30 +772,26 @@ pub fn edit_guild(guild_id: u64, map: &JsonMap) -> Result { /// Edits the positions of a guild's channels. pub fn edit_guild_channel_positions(guild_id: u64, value: &Value) -> Result<()> { - let body = serde_json::to_string(value)?; - - verify( - 204, - request!( - Route::GuildsIdChannels(guild_id), - patch(body), - "/guilds/{}/channels", - guild_id, - ), - ) + verify(204, request(Request { + body: Some(value.to_string().into_bytes()), + headers: None, + method: Method::Patch, + route: Route::GuildsIdChannels(guild_id), + url: api!("/guilds/{}/channels", guild_id), + })?) } /// Edits a [`Guild`]'s embed setting. /// /// [`Guild`]: ../model/guild/struct.Guild.html pub fn edit_guild_embed(guild_id: u64, map: &Value) -> Result { - let body = map.to_string(); - let response = request!( - Route::GuildsIdEmbed(guild_id), - patch(body), - "/guilds/{}/embed", - guild_id - ); + let response = request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Patch, + route: Route::GuildsIdEmbed(guild_id), + url: api!("/guilds/{}/embed", guild_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -804,32 +799,28 @@ pub fn edit_guild_embed(guild_id: u64, map: &Value) -> Result { /// Does specific actions to a member. pub fn edit_member(guild_id: u64, user_id: u64, map: &JsonMap) -> Result<()> { - let body = serde_json::to_string(map)?; - - verify( - 204, - request!( - Route::GuildsIdMembersId(guild_id), - patch(body), - "/guilds/{}/members/{}", - guild_id, - user_id - ), - ) + let body = serde_json::to_vec(map)?; + + verify(204, request(Request { + body: Some(body), + headers: None, + method: Method::Patch, + route: Route::GuildsIdMembersId(guild_id), + url: api!("/guilds/{}/members/{}", guild_id, user_id), + })?) } /// Edits a message by Id. /// /// **Note**: Only the author of a message can modify it. pub fn edit_message(channel_id: u64, message_id: u64, map: &Value) -> Result { - let body = map.to_string(); - let response = request!( - Route::ChannelsIdMessagesId(LightMethod::Any, channel_id), - patch(body), - "/channels/{}/messages/{}", - channel_id, - message_id - ); + let response = request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Patch, + route: Route::ChannelsIdMessagesId(LightMethod::Any, channel_id), + url: api!("/channels/{}/messages/{}", channel_id, message_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -842,15 +833,14 @@ pub fn edit_message(channel_id: u64, message_id: u64, map: &Value) -> Result) -> Result<()> { let map = json!({ "nick": new_nickname }); - let body = map.to_string(); - let response = request!( - Route::GuildsIdMembersMeNick(guild_id), - patch(body), - "/guilds/{}/members/@me/nick", - guild_id - ); - verify(200, response) + verify(200, request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Patch, + route: Route::GuildsIdMembersMeNick(guild_id), + url: api!("/guilds/{}/members/@me/nick", guild_id), + })?) } /// Edits the current user's profile settings. @@ -866,8 +856,15 @@ pub fn edit_nickname(guild_id: u64, new_nickname: Option<&str>) -> Result<()> { /// change and when the token is internally changed to be invalid requests, as /// the token may be outdated. pub fn edit_profile(map: &JsonMap) -> Result { - let body = serde_json::to_string(map)?; - let response = request!(Route::UsersMe, patch(body), "/users/@me"); + let body = serde_json::to_vec(map)?; + + let response = request(Request { + body: Some(body), + headers: None, + method: Method::Patch, + route: Route::UsersMe, + url: api!("/users/@me").to_owned(), + })?; let mut value = serde_json::from_reader::(response)?; @@ -885,14 +882,15 @@ pub fn edit_profile(map: &JsonMap) -> Result { /// Changes a role in a guild. pub fn edit_role(guild_id: u64, role_id: u64, map: &JsonMap) -> Result { - let body = serde_json::to_string(map)?; - let response = request!( - Route::GuildsIdRolesId(guild_id), - patch(body), - "/guilds/{}/roles/{}", - guild_id, - role_id - ); + let body = serde_json::to_vec(&map)?; + + let response = request(Request { + body: Some(body), + headers: None, + method: Method::Patch, + route: Route::GuildsIdRolesId(guild_id), + url: api!("/guilds/{}/roles/{}", guild_id, role_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -904,13 +902,14 @@ pub fn edit_role_position(guild_id: u64, role_id: u64, position: u64) -> Result< "id": role_id, "position": position, }))?; - let response = request!( - Route::GuildsIdRolesId(guild_id), - patch(body), - "/guilds/{}/roles/{}", - guild_id, - role_id - ); + + let response = request(Request { + body: Some(body.into_bytes()), + headers: None, + method: Method::Patch, + route: Route::GuildsIdRolesId(guild_id), + url: api!("/guilds/{}/roles/{}", guild_id, role_id), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -956,13 +955,13 @@ pub fn edit_role_position(guild_id: u64, role_id: u64, position: u64) -> Result< // The tests are ignored, rather than no_run'd, due to rustdoc tests with // external crates being incredibly messy and misleading in the end user's view. pub fn edit_webhook(webhook_id: u64, map: &Value) -> Result { - let body = map.to_string(); - let response = request!( - Route::WebhooksId(webhook_id), - patch(body), - "/webhooks/{}", - webhook_id, - ); + let response = request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Patch, + route: Route::WebhooksId(webhook_id), + url: api!("/webhooks/{}", webhook_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -995,13 +994,15 @@ pub fn edit_webhook(webhook_id: u64, map: &Value) -> Result { /// /// [`edit_webhook`]: fn.edit_webhook.html pub fn edit_webhook_with_token(webhook_id: u64, token: &str, map: &JsonMap) -> Result { - let body = serde_json::to_string(map)?; - - let response = retry(|| { - CLIENT - .patch(&format!(api!("/webhooks/{}/{}"), webhook_id, token)) - .body(&body) - }).map_err(Error::Hyper)?; + let body = serde_json::to_vec(map)?; + + let response = request(Request { + body: Some(body), + headers: None, + method: Method::Patch, + route: Route::None, + url: api!("/webhooks/{}/{}", webhook_id, token), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1072,21 +1073,20 @@ pub fn execute_webhook(webhook_id: u64, wait: bool, map: &JsonMap) -> Result> { - let body = serde_json::to_string(map)?; - - let response = retry(|| { - CLIENT - .post(&format!( - api!("/webhooks/{}/{}?wait={}"), - webhook_id, - token, - wait - )) - .body(&body) - .header(ContentType( - Mime(TopLevel::Application, SubLevel::Json, vec![]), - )) - }).map_err(Error::Hyper)?; + let body = serde_json::to_vec(map)?; + + let mut headers = Headers::new(); + headers.set(ContentType( + Mime(TopLevel::Application, SubLevel::Json, vec![]), + )); + + let response = request(Request { + body: Some(body), + headers: Some(headers), + method: Method::Get, + route: Route::None, + url: api!("/webhooks/{}/{}?wait={}", webhook_id, token, wait), + })?; if response.status == StatusCode::NoContent { return Ok(None); @@ -1101,8 +1101,12 @@ pub fn execute_webhook(webhook_id: u64, /// /// Does not require authentication. pub fn get_active_maintenances() -> Result> { - let response = retry(|| { - CLIENT.get(status!("/scheduled-maintenances/active.json")) + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::None, + url: status!("/scheduled-maintenances/active.json").to_owned(), })?; let mut map: BTreeMap = serde_json::from_reader(response)?; @@ -1116,12 +1120,13 @@ pub fn get_active_maintenances() -> Result> { /// Gets all the users that are banned in specific guild. pub fn get_bans(guild_id: u64) -> Result> { - let response = request!( - Route::GuildsIdBans(guild_id), - get, - "/guilds/{}/bans", - guild_id - ); + let response = request(Request { + body: None, + headers: None, + route: Route::GuildsIdBans(guild_id), + method: Method::Get, + url: api!("/guilds/{}/bans", guild_id), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1153,13 +1158,13 @@ pub fn get_audit_logs(guild_id: u64, query_string.insert(0, '?'); } - let response = request!( - Route::GuildsIdAuditLogs(guild_id), - get, - "/guilds/{}/audit-logs{}", - guild_id, - query_string - ); + let response = request(Request { + body: None, + headers: None, + route: Route::GuildsIdAuditLogs(guild_id), + method: Method::Get, + url: api!("/guilds/{}/audit-logs{}", guild_id, query_string), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1167,7 +1172,13 @@ pub fn get_audit_logs(guild_id: u64, /// Gets current bot gateway. pub fn get_bot_gateway() -> Result { - let response = request!(Route::GatewayBot, get, "/gateway/bot"); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::GatewayBot, + url: api!("/gateway/bot").to_owned(), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1175,12 +1186,13 @@ pub fn get_bot_gateway() -> Result { /// Gets all invites for a channel. pub fn get_channel_invites(channel_id: u64) -> Result> { - let response = request!( - Route::ChannelsIdInvites(channel_id), - get, - "/channels/{}/invites", - channel_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::ChannelsIdInvites(channel_id), + url: api!("/channels/{}/invites", channel_id), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1205,12 +1217,13 @@ pub fn get_channel_invites(channel_id: u64) -> Result> { /// /// [`GuildChannel`]: ../model/channel/struct.GuildChannel.html pub fn get_channel_webhooks(channel_id: u64) -> Result> { - let response = request!( - Route::ChannelsIdWebhooks(channel_id), - get, - "/channels/{}/webhooks", - channel_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::ChannelsIdWebhooks(channel_id), + url: api!("/channels/{}/webhooks", channel_id), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1218,12 +1231,13 @@ pub fn get_channel_webhooks(channel_id: u64) -> Result> { /// Gets channel information. pub fn get_channel(channel_id: u64) -> Result { - let response = request!( - Route::ChannelsId(channel_id), - get, - "/channels/{}", - channel_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::ChannelsId(channel_id), + url: api!("/channels/{}", channel_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1231,12 +1245,13 @@ pub fn get_channel(channel_id: u64) -> Result { /// Gets all channels in a guild. pub fn get_channels(guild_id: u64) -> Result> { - let response = request!( - Route::ChannelsId(guild_id), - get, - "/guilds/{}/channels", - guild_id - ); + let response = request(Request { + body: None, + headers: None, + route: Route::ChannelsId(guild_id), + method: Method::Get, + url: api!("/guilds/{}/channels", guild_id), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1246,7 +1261,13 @@ pub fn get_channels(guild_id: u64) -> Result> { /// /// **Note**: Only applications may use this endpoint. pub fn get_current_application_info() -> Result { - let response = request!(Route::None, get, "/oauth2/applications/@me"); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::None, + url: api!("/oauth2/applications/@me").to_owned(), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1254,7 +1275,13 @@ pub fn get_current_application_info() -> Result { /// Gets information about the user we're connected with. pub fn get_current_user() -> Result { - let response = request!(Route::UsersMe, get, "/users/@me"); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::UsersMe, + url: api!("/users/@me").to_owned(), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1262,7 +1289,13 @@ pub fn get_current_user() -> Result { /// Gets current gateway. pub fn get_gateway() -> Result { - let response = request!(Route::Gateway, get, "/gateway"); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::Gateway, + url: api!("/gateway").to_owned(), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1270,7 +1303,13 @@ pub fn get_gateway() -> Result { /// Gets guild information. pub fn get_guild(guild_id: u64) -> Result { - let response = request!(Route::GuildsId(guild_id), get, "/guilds/{}", guild_id); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::GuildsId(guild_id), + url: api!("/guilds/{}", guild_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1278,12 +1317,13 @@ pub fn get_guild(guild_id: u64) -> Result { /// Gets a guild embed information. pub fn get_guild_embed(guild_id: u64) -> Result { - let response = request!( - Route::GuildsIdEmbed(guild_id), - get, - "/guilds/{}/embeds", - guild_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::GuildsIdEmbed(guild_id), + url: api!("/guilds/{}/embeds", guild_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1291,12 +1331,13 @@ pub fn get_guild_embed(guild_id: u64) -> Result { /// Gets integrations that a guild has. pub fn get_guild_integrations(guild_id: u64) -> Result> { - let response = request!( - Route::GuildsIdIntegrations(guild_id), - get, - "/guilds/{}/integrations", - guild_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::GuildsIdIntegrations(guild_id), + url: api!("/guilds/{}/integrations", guild_id), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1304,12 +1345,13 @@ pub fn get_guild_integrations(guild_id: u64) -> Result> { /// Gets all invites to a guild. pub fn get_guild_invites(guild_id: u64) -> Result> { - let response = request!( - Route::GuildsIdInvites(guild_id), - get, - "/guilds/{}/invites", - guild_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::GuildsIdInvites(guild_id), + url: api!("/guilds/{}/invites", guild_id), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1322,12 +1364,13 @@ pub fn get_guild_vanity_url(guild_id: u64) -> Result { code: String, } - let response = request!( - Route::GuildsIdVanityUrl(guild_id), - get, - "/guilds/{}/vanity-url", - guild_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::GuildsIdVanityUrl(guild_id), + url: api!("/guilds/{}/vanity-url", guild_id), + })?; serde_json::from_reader::(response) .map(|x| x.code) @@ -1340,14 +1383,18 @@ pub fn get_guild_members(guild_id: u64, limit: Option, after: Option) -> Result> { - let response = request!( - Route::GuildsIdMembers(guild_id), - get, - "/guilds/{}/members?limit={}&after={}", - guild_id, - limit.unwrap_or(500), - after.unwrap_or(0) - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::GuildsIdMembers(guild_id), + url: api!( + "/guilds/{}/members?limit={}&after={}", + guild_id, + limit.unwrap_or(500), + after.unwrap_or(0), + ), + })?; let mut v = serde_json::from_reader::(response)?; @@ -1366,13 +1413,13 @@ pub fn get_guild_members(guild_id: u64, /// Gets the amount of users that can be pruned. pub fn get_guild_prune_count(guild_id: u64, map: &Value) -> Result { - let body = map.to_string(); - let response = request!( - Route::GuildsIdPrune(guild_id), - get(body), - "/guilds/{}/prune", - guild_id - ); + let response = request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Get, + route: Route::GuildsIdPrune(guild_id), + url: api!("/guilds/{}/prune", guild_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1381,12 +1428,13 @@ pub fn get_guild_prune_count(guild_id: u64, map: &Value) -> Result { /// Gets regions that a guild can use. If a guild has the `VIP_REGIONS` feature /// enabled, then additional VIP-only regions are returned. pub fn get_guild_regions(guild_id: u64) -> Result> { - let response = request!( - Route::GuildsIdRegions(guild_id), - get, - "/guilds/{}/regions", - guild_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::GuildsIdRegions(guild_id), + url: api!("/guilds/{}/regions", guild_id), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1396,12 +1444,13 @@ pub fn get_guild_regions(guild_id: u64) -> Result> { /// /// [`Guild`]: ../model/guild/struct.Guild.html pub fn get_guild_roles(guild_id: u64) -> Result> { - let response = request!( - Route::GuildsIdRoles(guild_id), - get, - "/guilds/{}/roles", - guild_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::GuildsIdRoles(guild_id), + url: api!("/guilds/{}/roles", guild_id), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1426,12 +1475,13 @@ pub fn get_guild_roles(guild_id: u64) -> Result> { /// /// [`Guild`]: ../model/guild/struct.Guild.html pub fn get_guild_webhooks(guild_id: u64) -> Result> { - let response = request!( - Route::GuildsIdWebhooks(guild_id), - get, - "/guilds/{}/webhooks", - guild_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::GuildsIdWebhooks(guild_id), + url: api!("/guilds/{}/webhooks", guild_id), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1469,7 +1519,13 @@ pub fn get_guilds(target: &GuildPagination, limit: u64) -> Result }, } - let response = request!(Route::UsersMeGuilds, get, "{}", uri); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::UsersMeGuilds, + url: api!("{}", uri), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1491,7 +1547,13 @@ pub fn get_invite(code: &str, stats: bool) -> Result { uri.push_str("?with_counts=true"); } - let response = request!(Route::InvitesCode, get, "{}", uri); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::InvitesCode, + url: api!("{}", uri), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1499,13 +1561,13 @@ pub fn get_invite(code: &str, stats: bool) -> Result { /// Gets member of a guild. pub fn get_member(guild_id: u64, user_id: u64) -> Result { - let response = request!( - Route::GuildsIdMembersId(guild_id), - get, - "/guilds/{}/members/{}", - guild_id, - user_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::GuildsIdMembersId(guild_id), + url: api!("/guilds/{}/members/{}", guild_id, user_id), + })?; let mut v = serde_json::from_reader::(response)?; @@ -1518,13 +1580,13 @@ pub fn get_member(guild_id: u64, user_id: u64) -> Result { /// Gets a message by an Id, bots only. pub fn get_message(channel_id: u64, message_id: u64) -> Result { - let response = request!( - Route::ChannelsIdMessagesId(LightMethod::Any, channel_id), - get, - "/channels/{}/messages/{}", - channel_id, - message_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::ChannelsIdMessagesId(LightMethod::Any, channel_id), + url: api!("/channels/{}/messages/{}", channel_id, message_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1532,9 +1594,13 @@ pub fn get_message(channel_id: u64, message_id: u64) -> Result { /// Gets X messages from a channel. pub fn get_messages(channel_id: u64, query: &str) -> Result> { - let url = format!(api!("/channels/{}/messages{}"), channel_id, query); - - let response = request(Route::ChannelsIdMessages(channel_id), || CLIENT.get(&url))?; + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::ChannelsIdMessages(channel_id), + url: api!("/channels/{}/messages{}", channel_id, query), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1542,12 +1608,13 @@ pub fn get_messages(channel_id: u64, query: &str) -> Result> { /// Gets all pins of a channel. pub fn get_pins(channel_id: u64) -> Result> { - let response = request!( - Route::ChannelsIdPins(channel_id), - get, - "/channels/{}/pins", - channel_id - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::ChannelsIdPins(channel_id), + url: api!("/channels/{}/pins", channel_id), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1572,12 +1639,13 @@ pub fn get_reaction_users(channel_id: u64, write!(uri, "&after={}", user_id)?; } - let response = request!( - Route::ChannelsIdMessagesIdReactionsUserIdType(channel_id), - get, - "{}", - uri - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::ChannelsIdMessagesIdReactionsUserIdType(channel_id), + url: api!("{}", uri), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1587,7 +1655,13 @@ pub fn get_reaction_users(channel_id: u64, /// /// Does not require authentication. pub fn get_unresolved_incidents() -> Result> { - let response = retry(|| CLIENT.get(status!("/incidents/unresolved.json")))?; + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::None, + url: status!("/incidents/unresolved.json").to_owned(), + })?; let mut map: BTreeMap = serde_json::from_reader(response)?; @@ -1602,8 +1676,12 @@ pub fn get_unresolved_incidents() -> Result> { /// /// Does not require authentication. pub fn get_upcoming_maintenances() -> Result> { - let response = retry(|| { - CLIENT.get(status!("/scheduled-maintenances/upcoming.json")) + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::None, + url: status!("/scheduled-maintenances/upcoming.json").to_owned(), })?; let mut map: BTreeMap = serde_json::from_reader(response)?; @@ -1617,7 +1695,13 @@ pub fn get_upcoming_maintenances() -> Result> { /// Gets a user by Id. pub fn get_user(user_id: u64) -> Result { - let response = request!(Route::UsersId, get, "/users/{}", user_id); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::UsersId, + url: api!("/users/{}", user_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1625,7 +1709,13 @@ pub fn get_user(user_id: u64) -> Result { /// Gets our DM channels. pub fn get_user_dm_channels() -> Result> { - let response = request!(Route::UsersMeChannels, get, "/users/@me/channels"); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::UsersMeChannels, + url: api!("/users/@me/channels").to_owned(), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1633,7 +1723,13 @@ pub fn get_user_dm_channels() -> Result> { /// Gets all voice regions. pub fn get_voice_regions() -> Result> { - let response = request!(Route::VoiceRegions, get, "/voice/regions"); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::VoiceRegions, + url: api!("/voice/regions").to_owned(), + })?; serde_json::from_reader::>(response) .map_err(From::from) @@ -1657,12 +1753,13 @@ pub fn get_voice_regions() -> Result> { /// /// [`get_webhook_with_token`]: fn.get_webhook_with_token.html pub fn get_webhook(webhook_id: u64) -> Result { - let response = request!( - Route::WebhooksId(webhook_id), - get, - "/webhooks/{}", - webhook_id, - ); + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::WebhooksId(webhook_id), + url: api!("/webhooks/{}", webhook_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1686,9 +1783,13 @@ pub fn get_webhook(webhook_id: u64) -> Result { /// .expect("Error getting webhook"); /// ``` pub fn get_webhook_with_token(webhook_id: u64, token: &str) -> Result { - let response = retry(|| { - CLIENT.get(&format!(api!("/webhooks/{}/{}"), webhook_id, token)) - }).map_err(Error::Hyper)?; + let response = request(Request { + body: None, + headers: None, + method: Method::Get, + route: Route::None, + url: api!("/webhooks/{}/{}", webhook_id, token), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1696,21 +1797,24 @@ pub fn get_webhook_with_token(webhook_id: u64, token: &str) -> Result { /// Kicks a member from a guild. pub fn kick_member(guild_id: u64, user_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::GuildsIdMembersId(guild_id), - delete, - "/guilds/{}/members/{}", - guild_id, - user_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::GuildsIdMembersId(guild_id), + url: api!("/guilds/{}/members/{}", guild_id, user_id), + })?) } /// Leaves a group DM. pub fn leave_group(guild_id: u64) -> Result { - let response = request!(Route::None, delete, "/channels/{}", guild_id); + let response = request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::None, + url: api!("/channels/{}", guild_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1718,29 +1822,24 @@ pub fn leave_group(guild_id: u64) -> Result { /// Leaves a guild. pub fn leave_guild(guild_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::UsersMeGuildsId, - delete, - "/users/@me/guilds/{}", - guild_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::UsersMeGuildsId, + url: api!("/users/@me/guilds/{}", guild_id), + })?) } /// Deletes a user from group DM. pub fn remove_group_recipient(group_id: u64, user_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::None, - delete, - "/channels/{}/recipients/{}", - group_id, - user_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::None, + url: api!("/channels/{}/recipients/{}", group_id, user_id), + })?) } /// Sends file(s) to a channel. @@ -1754,7 +1853,7 @@ pub fn remove_group_recipient(group_id: u64, user_id: u64) -> Result<()> { /// [`HttpError::InvalidRequest`]: enum.HttpError.html#variant.InvalidRequest pub fn send_files<'a, T, It: IntoIterator>(channel_id: u64, files: It, map: JsonMap) -> Result where T: Into> { - let uri = format!(api!("/channels/{}/messages"), channel_id); + let uri = api!("/channels/{}/messages", channel_id); let url = match Url::parse(&uri) { Ok(url) => url, Err(_) => return Err(Error::Url(uri)), @@ -1762,7 +1861,7 @@ pub fn send_files<'a, T, It: IntoIterator>(channel_id: u64, files: It, m let tc = NativeTlsClient::new()?; let connector = HttpsConnector::new(tc); - let mut request = Request::with_connector(Method::Post, url, &connector)?; + let mut request = HyperRequest::with_connector(Method::Post, url, &connector)?; request .headers_mut() .set(header::Authorization(TOKEN.lock().clone())); @@ -1817,13 +1916,13 @@ pub fn send_files<'a, T, It: IntoIterator>(channel_id: u64, files: It, m /// Sends a message to a channel. pub fn send_message(channel_id: u64, map: &Value) -> Result { - let body = map.to_string(); - let response = request!( - Route::ChannelsIdMessages(channel_id), - post(body), - "/channels/{}/messages", - channel_id - ); + let response = request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Post, + route: Route::ChannelsIdMessages(channel_id), + url: api!("/channels/{}/messages", channel_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1831,30 +1930,24 @@ pub fn send_message(channel_id: u64, map: &Value) -> Result { /// Pins a message in a channel. pub fn pin_message(channel_id: u64, message_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::ChannelsIdPinsMessageId(channel_id), - put, - "/channels/{}/pins/{}", - channel_id, - message_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Put, + route: Route::ChannelsIdPinsMessageId(channel_id), + url: api!("/channels/{}/pins/{}", channel_id, message_id), + })?) } /// Unbans a user from a guild. pub fn remove_ban(guild_id: u64, user_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::GuildsIdBansUserId(guild_id), - delete, - "/guilds/{}/bans/{}", - guild_id, - user_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::GuildsIdBansUserId(guild_id), + url: api!("/guilds/{}/bans/{}", guild_id, user_id), + })?) } /// Deletes a single [`Role`] from a [`Member`] in a [`Guild`]. @@ -1867,28 +1960,24 @@ pub fn remove_ban(guild_id: u64, user_id: u64) -> Result<()> { /// [`Role`]: ../model/guild/struct.Role.html /// [Manage Roles]: ../model/permissions/constant.MANAGE_ROLES.html pub fn remove_member_role(guild_id: u64, user_id: u64, role_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::GuildsIdMembersIdRolesId(guild_id), - delete, - "/guilds/{}/members/{}/roles/{}", - guild_id, - user_id, - role_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::GuildsIdMembersIdRolesId(guild_id), + url: api!("/guilds/{}/members/{}/roles/{}", guild_id, user_id, role_id), + })?) } /// Starts removing some members from a guild based on the last time they've been online. pub fn start_guild_prune(guild_id: u64, map: &Value) -> Result { - let body = map.to_string(); - let response = request!( - Route::GuildsIdPrune(guild_id), - post(body), - "/guilds/{}/prune", - guild_id - ); + let response = request(Request { + body: Some(map.to_string().into_bytes()), + headers: None, + method: Method::Post, + route: Route::GuildsIdPrune(guild_id), + url: api!("/guilds/{}/prune", guild_id), + })?; serde_json::from_reader::(response) .map_err(From::from) @@ -1896,38 +1985,28 @@ pub fn start_guild_prune(guild_id: u64, map: &Value) -> Result { /// Starts syncing an integration with a guild. pub fn start_integration_sync(guild_id: u64, integration_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::GuildsIdIntegrationsIdSync(guild_id), - post, - "/guilds/{}/integrations/{}/sync", - guild_id, - integration_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Post, + route: Route::GuildsIdIntegrationsIdSync(guild_id), + url: api!("/guilds/{}/integrations/{}/sync", guild_id, integration_id), + })?) } /// Unpins a message from a channel. pub fn unpin_message(channel_id: u64, message_id: u64) -> Result<()> { - verify( - 204, - request!( - Route::ChannelsIdPinsMessageId(channel_id), - delete, - "/channels/{}/pins/{}", - channel_id, - message_id - ), - ) + verify(204, request(Request { + body: None, + headers: None, + method: Method::Delete, + route: Route::ChannelsIdPinsMessageId(channel_id), + url: api!("/channels/{}/pins/{}", channel_id, message_id), + })?) } -fn request<'a, F>(route: Route, f: F) -> Result - where F: Fn() -> RequestBuilder<'a> { - let response = ratelimiting::perform(route, || { - f().header(header::Authorization(TOKEN.lock().clone())) - .header(header::ContentType::json()) - })?; +fn request(req: Request) -> Result { + let response = ratelimiting::perform(req)?; if response.status.class() == StatusClass::Success { Ok(response) @@ -1936,17 +2015,33 @@ fn request<'a, F>(route: Route, f: F) -> Result } } -pub(crate) fn retry<'a, F>(f: F) -> HyperResult - where F: Fn() -> RequestBuilder<'a> { - let req = || { - f().header(header::UserAgent(constants::USER_AGENT.to_string())) - .send() - }; +fn build_req(req: &Request) -> RequestBuilder { + let mut builder = CLIENT.request(req.method.clone(), &req.url); + + if let Some(ref bytes) = req.body { + builder = builder.body(HyperBody::BufBody(bytes, bytes.len())); + } - match req() { - Err(HyperError::Io(ref io)) if io.kind() == IoErrorKind::ConnectionAborted => req(), - other => other, + if let Some(headers) = req.headers.clone() { + builder = builder.headers(headers); } + + builder +} + +fn retry<'a>(req: &Request) -> HyperResult { + // Retry the request twice in a loop until it succeeds. + // + // If it doesn't and the loop breaks, try one last time. + for _ in 0..=2 { + match build_req(req).send() { + Err(HyperError::Io(ref io)) + if io.kind() == IoErrorKind::ConnectionAborted => continue, + other => return other, + } + } + + build_req(req).send() } fn verify(expected: u16, response: HyperResponse) -> Result<()> { diff --git a/src/http/ratelimiting.rs b/src/http/ratelimiting.rs index b8152d17a06..1c31c9f8169 100644 --- a/src/http/ratelimiting.rs +++ b/src/http/ratelimiting.rs @@ -41,7 +41,7 @@ #![allow(zero_ptr)] use chrono::{DateTime, Utc}; -use hyper::client::{RequestBuilder, Response}; +use hyper::client::Response; use hyper::header::Headers; use hyper::status::StatusCode; use internal::prelude::*; @@ -54,7 +54,7 @@ use std::{ thread, i64 }; -use super::{HttpError, LightMethod}; +use super::{HttpError, LightMethod, Request}; /// Refer to [`offset`]. /// @@ -361,8 +361,7 @@ pub enum Route { None, } -pub(crate) fn perform<'a, F>(route: Route, f: F) -> Result - where F: Fn() -> RequestBuilder<'a> { +pub(super) fn perform(req: Request) -> Result { loop { // This will block if another thread already has the global // unlocked already (due to receiving an x-ratelimit-global). @@ -378,7 +377,7 @@ pub(crate) fn perform<'a, F>(route: Route, f: F) -> Result // - then, perform the request let bucket = Arc::clone(ROUTES .lock() - .entry(route) + .entry(req.route) .or_insert_with(|| { Arc::new(Mutex::new(RateLimit { limit: i64::MAX, @@ -388,9 +387,9 @@ pub(crate) fn perform<'a, F>(route: Route, f: F) -> Result })); let mut lock = bucket.lock(); - lock.pre_hook(&route); + lock.pre_hook(&req.route); - let response = super::retry(&f)?; + let response = super::retry(&req)?; // Check if an offset has been calculated yet to determine the time // difference from Discord can the client. @@ -416,7 +415,7 @@ pub(crate) fn perform<'a, F>(route: Route, f: F) -> Result // It _may_ be possible for the limit to be raised at any time, // so check if it did from the value of the 'x-ratelimit-limit' // header. If the limit was 5 and is now 7, add 2 to the 'remaining' - if route == Route::None { + if req.route == Route::None { return Ok(response); } else { let redo = if response.headers.get_raw("x-ratelimit-global").is_some() { @@ -424,7 +423,7 @@ pub(crate) fn perform<'a, F>(route: Route, f: F) -> Result Ok( if let Some(retry_after) = parse_header(&response.headers, "retry-after")? { - debug!("Ratelimited on route {:?} for {:?}ms", route, retry_after); + debug!("Ratelimited on route {:?} for {:?}ms", req.route, retry_after); thread::sleep(Duration::from_millis(retry_after as u64)); true @@ -433,7 +432,7 @@ pub(crate) fn perform<'a, F>(route: Route, f: F) -> Result }, ) } else { - lock.post_hook(&response, &route) + lock.post_hook(&response, &req.route) }; if !redo.unwrap_or(true) { diff --git a/src/internal/macros.rs b/src/internal/macros.rs index 6d827047538..31b6b7de88e 100644 --- a/src/internal/macros.rs +++ b/src/internal/macros.rs @@ -1,27 +1,5 @@ //! A set of macros for easily working with internals. -#[cfg(feature = "http")] -macro_rules! request { - ($route:expr, $method:ident($body:expr), $url:expr, $($rest:tt)*) => {{ - request($route, || CLIENT - .$method(&format!(api!($url), $($rest)*)) - .body(&$body))? - }}; - ($route:expr, $method:ident($body:expr), $url:expr) => {{ - request($route, || CLIENT - .$method(api!($url)) - .body(&$body))? - }}; - ($route:expr, $method:ident, $url:expr, $($rest:tt)*) => {{ - request($route, || CLIENT - .$method(&format!(api!($url), $($rest)*)))? - }}; - ($route:expr, $method:ident, $url:expr) => {{ - request($route, || CLIENT - .$method(api!($url)))? - }}; -} - #[cfg(feature = "http")] macro_rules! request_client { () => {{