-
-
Notifications
You must be signed in to change notification settings - Fork 131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom HTTP Response abstraction #923
Merged
zeylahellyer
merged 7 commits into
twilight-rs:next
from
zeylahellyer:feature/http/response
Jul 3, 2021
Merged
Custom HTTP Response abstraction #923
zeylahellyer
merged 7 commits into
twilight-rs:next
from
zeylahellyer:feature/http/response
Jul 3, 2021
Commits on Jul 3, 2021
-
feat!(http): custom response abstractions
Our HTTP async request builders return a deserialized model in the output of their `Future` implementations, but only if the response has an expected body. For example, the `GetUser` request's `Future` implementation returns a deserialized `User`. `GetGuildRoles`' `Future` implementations returns `Vec<Role>`. This design has three problems worth noting: 1. It's impossible to know the headers and status code of a response: it may be useful to check if the response is a success (2xx status code range) prior to deserializing; and certain headers may be useful for users to know; 2. Not all responses are used: in many instances of creating a message the created message is not used, so we're spending CPU time on a useless operation; and 3. Response bodies with a list will entirely fail if one entry fails to deserialize. By creating a custom `Response` type we can solve all three of these problems. Take the first example: checking the status code prior to deserializing a response body. With a `Response` struct this is easy: ```rust use std::env; use twilight_http::Client; let client = Client::new(env::var("DISCORD_TOKEN")?); let response = client.user(user_id).await?; if !response.status().is_success() { println!("failed to get user"); return Ok(()); } // Twilight already knows to deserialize it into a // `twilight_model::user::User`. let user = response.model().await?; println!("user's name: {}:{}", user.name, user.discriminator); ``` In this example it's easy to check if the status code of the response is in the success range (2xx). Looking at the example, we can see how the second issue is solved: you can simply opt to not deserialize the model. This may not be useful for getting a user, but can be for creating a message: ```rust use std::env; use twilight_http::Client; let client = Client::new(env::var("DISCORD_TOKEN")?); client.create_message(channel_id) .content("test")? .await?; ``` Here we create a message and return an error if the request failed. We don't care about the message itself, so the response is discarded without deserializing it. The final problem of one failure in a list causing the deserialization of the entire list to fail can be solved with a lazily deserializing iterator. Instead of deserializing everything up front, we can use an iterator which will deserialize entries as the iterator is advanced. We can see how that looks like when retrieving the roles of a guild and printing their names: ```rust use std::env; use twilight_http::Client; let client = Client::new(env::var("DISCORD_TOKEN")?); let response = client.roles(guild_id).await?; // Create an iterator over the roles. Each role in the response body is only // deserialized as the iterator advances. let mut roles = response.iter().await?; while let Some(maybe_role) = roles.next() { // The role may not have deserialized properly, so we need to check. match maybe_role { Ok(role) => println!("role {}'s name: {}", role.id, role.name), Err(source) => println!("failed to deserialize role: {:?}", source), } } ``` The `Response` struct also has a few other cool functions: `bytes` which returns a `Future` that resolves to a `Vec<u8>` of the bytes of the response body, `text` which returns a String of the body if it's UTF-8 valid, and `headers` which returns an iterator of the names and values of the response headers. In the simplest case, this PR causes users that want the returned deserialized model to append `.model().await?` or `.models().await?` to their request calls. Signed-off-by: Vivian Hellyer <vivian@hellyer.dev>
Configuration menu - View commit details
-
Copy full SHA for 6443f9a - Browse repository at this point
Copy the full SHA 6443f9aView commit details -
Signed-off-by: Vivian Hellyer <vivian@hellyer.dev>
Configuration menu - View commit details
-
Copy full SHA for 8aae832 - Browse repository at this point
Copy the full SHA 8aae832View commit details -
remove option from pending response types
Signed-off-by: Vivian Hellyer <vivian@hellyer.dev>
Configuration menu - View commit details
-
Copy full SHA for 90162a5 - Browse repository at this point
Copy the full SHA 90162a5View commit details -
remove response body iter for now
`serde_json::StreamDeserializer` doesn't work by consuming bodies like `[{"a": 1}, {"b": 2}]`, it instead works by consuming bodies like: ```json {"a": 1} {"b": 2} ``` Working around this in a performant way will take some work, so for now I am removing `MemberIter` and `ModelIter` support. Signed-off-by: Vivian Hellyer <vivian@hellyer.dev>
Configuration menu - View commit details
-
Copy full SHA for db7ba9b - Browse repository at this point
Copy the full SHA db7ba9bView commit details -
Signed-off-by: Vivian Hellyer <vivian@hellyer.dev>
Configuration menu - View commit details
-
Copy full SHA for a2d431c - Browse repository at this point
Copy the full SHA a2d431cView commit details -
Configuration menu - View commit details
-
Copy full SHA for c61852e - Browse repository at this point
Copy the full SHA c61852eView commit details -
add some non exhaustives to body markers
Signed-off-by: Zeyla Hellyer <zeyla@hellyer.dev>
Configuration menu - View commit details
-
Copy full SHA for 03d95a2 - Browse repository at this point
Copy the full SHA 03d95a2View commit details
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.