Skip to content

Commit

Permalink
http: add optional future outputs for requests (#187)
Browse files Browse the repository at this point in the history
When the user uses a retrieval request, like getting a user by ID, the
future output is a `Result<Option<T>>`. The code to handle 404 response
status codes didn't actually exist, so it'd always either be
`Ok(Some(T))` or `Err(E)`.

This patch adds support for handling 404 responses and returning
`Ok(None)` where encountered on select retrieval routes.

Here's an example of output:

```
Getting guild member for user 1
Ok(None)
Getting guild member for user 640434332485287936
Ok(Some(Member { deaf: false, guild_id: None, hoisted_role: Non.....
```

Closes #157.

Approved-by: AEnterprise <[email protected]>
Approved-by: Valdemar Erk <[email protected]>
Merged-by: Valdemar Erk <[email protected]>
Signed-off-by: Vivian Hellyer <[email protected]>
  • Loading branch information
zeylahellyer authored Jun 5, 2020
1 parent b212c44 commit 48a4ae8
Show file tree
Hide file tree
Showing 14 changed files with 170 additions and 74 deletions.
1 change: 1 addition & 0 deletions http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ repository = "https://github.com/twilight-rs/twilight.git"
version = "0.1.0"

[dependencies]
bytes = { default-features = false, version = "0.5" }
futures = "0.3"
twilight-model = { path = "../model" }
log = "0.4"
Expand Down
9 changes: 9 additions & 0 deletions http/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::{
Request,
},
};
use bytes::Bytes;
use log::{debug, warn};
use reqwest::{
header::HeaderValue, Body, Client as ReqwestClient, ClientBuilder as ReqwestClientBuilder,
Expand Down Expand Up @@ -845,6 +846,14 @@ impl Client {
})
}

pub(crate) async fn request_bytes(&self, request: Request) -> Result<Bytes> {
let resp = self.make_request(request).await?;

resp.bytes()
.await
.map_err(|source| Error::ChunkingResponse { source })
}

pub async fn verify(&self, request: Request) -> Result<()> {
self.make_request(request).await?;

Expand Down
15 changes: 8 additions & 7 deletions http/src/request/channel/get_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use twilight_model::{channel::Channel, id::ChannelId};

pub struct GetChannel<'a> {
channel_id: ChannelId,
fut: Option<Pending<'a, Option<Channel>>>,
fut: Option<PendingOption<'a>>,
http: &'a Client,
}

Expand All @@ -17,14 +17,15 @@ impl<'a> GetChannel<'a> {
}

fn start(&mut self) -> Result<()> {
self.fut.replace(Box::pin(self.http.request(Request::from(
Route::GetChannel {
channel_id: self.channel_id.0,
},
))));
self.fut
.replace(Box::pin(self.http.request_bytes(Request::from(
Route::GetChannel {
channel_id: self.channel_id.0,
},
))));

Ok(())
}
}

poll_req!(GetChannel<'_>, Option<Channel>);
poll_req!(opt, GetChannel<'_>, Channel);
17 changes: 9 additions & 8 deletions http/src/request/channel/invite/get_invite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ struct GetInviteFields {
pub struct GetInvite<'a> {
code: String,
fields: GetInviteFields,
fut: Option<Pending<'a, Option<Invite>>>,
fut: Option<PendingOption<'a>>,
http: &'a Client,
}

Expand All @@ -30,15 +30,16 @@ impl<'a> GetInvite<'a> {
}

fn start(&mut self) -> Result<()> {
self.fut.replace(Box::pin(self.http.request(Request::from(
Route::GetInvite {
code: self.code.clone(),
with_counts: self.fields.with_counts,
},
))));
self.fut
.replace(Box::pin(self.http.request_bytes(Request::from(
Route::GetInvite {
code: self.code.clone(),
with_counts: self.fields.with_counts,
},
))));

Ok(())
}
}

poll_req!(GetInvite<'_>, Option<Invite>);
poll_req!(opt, GetInvite<'_>, Invite);
17 changes: 9 additions & 8 deletions http/src/request/channel/message/get_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use twilight_model::{

pub struct GetMessage<'a> {
channel_id: ChannelId,
fut: Option<Pending<'a, Option<Message>>>,
fut: Option<PendingOption<'a>>,
http: &'a Client,
message_id: MessageId,
}
Expand All @@ -22,15 +22,16 @@ impl<'a> GetMessage<'a> {
}

fn start(&mut self) -> Result<()> {
self.fut.replace(Box::pin(self.http.request(Request::from(
Route::GetMessage {
channel_id: self.channel_id.0,
message_id: self.message_id.0,
},
))));
self.fut
.replace(Box::pin(self.http.request_bytes(Request::from(
Route::GetMessage {
channel_id: self.channel_id.0,
message_id: self.message_id.0,
},
))));

Ok(())
}
}

poll_req!(GetMessage<'_>, Option<Message>);
poll_req!(opt, GetMessage<'_>, Message);
17 changes: 9 additions & 8 deletions http/src/request/channel/webhook/get_webhook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ struct GetWebhookFields {

pub struct GetWebhook<'a> {
fields: GetWebhookFields,
fut: Option<Pending<'a, Option<Webhook>>>,
fut: Option<PendingOption<'a>>,
http: &'a Client,
id: WebhookId,
}
Expand All @@ -30,15 +30,16 @@ impl<'a> GetWebhook<'a> {
}

fn start(&mut self) -> Result<()> {
self.fut.replace(Box::pin(self.http.request(Request::from(
Route::GetWebhook {
token: self.fields.token.clone(),
webhook_id: self.id.0,
},
))));
self.fut
.replace(Box::pin(self.http.request_bytes(Request::from(
Route::GetWebhook {
token: self.fields.token.clone(),
webhook_id: self.id.0,
},
))));

Ok(())
}
}

poll_req!(GetWebhook<'_>, Option<Webhook>);
poll_req!(opt, GetWebhook<'_>, Webhook);
14 changes: 8 additions & 6 deletions http/src/request/guild/ban/get_ban.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use twilight_model::{
};

pub struct GetBan<'a> {
fut: Option<Pending<'a, Option<Ban>>>,
fut: Option<PendingOption<'a>>,
guild_id: GuildId,
http: &'a Client,
user_id: UserId,
Expand All @@ -23,13 +23,15 @@ impl<'a> GetBan<'a> {

fn start(&mut self) -> Result<()> {
self.fut
.replace(Box::pin(self.http.request(Request::from(Route::GetBan {
guild_id: self.guild_id.0,
user_id: self.user_id.0,
}))));
.replace(Box::pin(self.http.request_bytes(Request::from(
Route::GetBan {
guild_id: self.guild_id.0,
user_id: self.user_id.0,
},
))));

Ok(())
}
}

poll_req!(GetBan<'_>, Option<Ban>);
poll_req!(opt, GetBan<'_>, Ban);
17 changes: 9 additions & 8 deletions http/src/request/guild/emoji/get_emoji.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use twilight_model::{

pub struct GetEmoji<'a> {
emoji_id: EmojiId,
fut: Option<Pending<'a, Option<Emoji>>>,
fut: Option<PendingOption<'a>>,
guild_id: GuildId,
http: &'a Client,
}
Expand All @@ -22,15 +22,16 @@ impl<'a> GetEmoji<'a> {
}

fn start(&mut self) -> Result<()> {
self.fut.replace(Box::pin(self.http.request(Request::from(
Route::GetEmoji {
emoji_id: self.emoji_id.0,
guild_id: self.guild_id.0,
},
))));
self.fut
.replace(Box::pin(self.http.request_bytes(Request::from(
Route::GetEmoji {
emoji_id: self.emoji_id.0,
guild_id: self.guild_id.0,
},
))));

Ok(())
}
}

poll_req!(GetEmoji<'_>, Option<Emoji>);
poll_req!(opt, GetEmoji<'_>, Emoji);
49 changes: 42 additions & 7 deletions http/src/request/guild/get_guild_vanity_url.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use crate::request::prelude::*;
use futures::TryFutureExt;
use crate::{request::prelude::*, Error};
use serde::Deserialize;
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use twilight_model::id::GuildId;

#[derive(Deserialize)]
Expand All @@ -9,7 +13,7 @@ struct VanityUrl {
}

pub struct GetGuildVanityUrl<'a> {
fut: Option<Pending<'a, String>>,
fut: Option<PendingOption<'a>>,
guild_id: GuildId,
http: &'a Client,
}
Expand All @@ -26,14 +30,45 @@ impl<'a> GetGuildVanityUrl<'a> {
fn start(&mut self) -> Result<()> {
let fut = self
.http
.request::<VanityUrl>(Request::from(Route::GetGuildVanityUrl {
.request_bytes(Request::from(Route::GetGuildVanityUrl {
guild_id: self.guild_id.0,
}))
.map_ok(|url| url.code);
}));
self.fut.replace(Box::pin(fut));

Ok(())
}
}

poll_req!(GetGuildVanityUrl<'_>, String);
impl Future for GetGuildVanityUrl<'_> {
type Output = Result<Option<String>>;

fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
if let Some(fut) = self.as_mut().fut.as_mut() {
let bytes = match fut.as_mut().poll(cx) {
Poll::Ready(Ok(bytes)) => bytes,
Poll::Ready(Err(crate::Error::Response { status, .. }))
if status == reqwest::StatusCode::NOT_FOUND =>
{
return Poll::Ready(Ok(None));
}
Poll::Ready(Err(why)) => return Poll::Ready(Err(why)),
Poll::Pending => return Poll::Pending,
};

let vanity_url = serde_json::from_slice::<VanityUrl>(&bytes).map_err(|source| {
Error::Parsing {
body: bytes.to_vec(),
source,
}
})?;

return Poll::Ready(Ok(Some(vanity_url.code)));
}

if let Err(why) = self.as_mut().start() {
return Poll::Ready(Err(why));
}
}
}
}
15 changes: 8 additions & 7 deletions http/src/request/guild/get_guild_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::request::prelude::*;
use twilight_model::{guild::GuildWidget, id::GuildId};

pub struct GetGuildWidget<'a> {
fut: Option<Pending<'a, Option<GuildWidget>>>,
fut: Option<PendingOption<'a>>,
guild_id: GuildId,
http: &'a Client,
}
Expand All @@ -17,14 +17,15 @@ impl<'a> GetGuildWidget<'a> {
}

fn start(&mut self) -> Result<()> {
self.fut.replace(Box::pin(self.http.request(Request::from(
Route::GetGuildWidget {
guild_id: self.guild_id.0,
},
))));
self.fut
.replace(Box::pin(self.http.request_bytes(Request::from(
Route::GetGuildWidget {
guild_id: self.guild_id.0,
},
))));

Ok(())
}
}

poll_req!(GetGuildWidget<'_>, Option<GuildWidget>);
poll_req!(opt, GetGuildWidget<'_>, GuildWidget);
17 changes: 9 additions & 8 deletions http/src/request/guild/member/get_member.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use twilight_model::{
};

pub struct GetMember<'a> {
fut: Option<Pending<'a, Option<Member>>>,
fut: Option<PendingOption<'a>>,
guild_id: GuildId,
http: &'a Client,
user_id: UserId,
Expand All @@ -22,15 +22,16 @@ impl<'a> GetMember<'a> {
}

fn start(&mut self) -> Result<()> {
self.fut.replace(Box::pin(self.http.request(Request::from(
Route::GetMember {
guild_id: self.guild_id.0,
user_id: self.user_id.0,
},
))));
self.fut
.replace(Box::pin(self.http.request_bytes(Request::from(
Route::GetMember {
guild_id: self.guild_id.0,
user_id: self.user_id.0,
},
))));

Ok(())
}
}

poll_req!(GetMember<'_>, Option<Member>);
poll_req!(opt, GetMember<'_>, Member);
Loading

3 comments on commit 48a4ae8

@zeylahellyer
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Build broken by a regression into stable clippy

@Erk-
Copy link
Member

@Erk- Erk- commented on 48a4ae8 Jun 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference this is the bug rust-lang/rust-clippy#5360

@Erk-
Copy link
Member

@Erk- Erk- commented on 48a4ae8 Jun 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it will not be fixed before 1.45 rust-lang/rust-clippy#5535 (comment)

Please sign in to comment.