Skip to content

Commit dd4efa8

Browse files
AndyGaugepollosp
andauthored
Async reqwest (#597)
* update reqwest and tokio * Make extract link working with new packages * Make uniq working with new packages * Update get.md * Make rest-post working with new packages * Make API resource exists working with new packages * Make Query the GitHub API working with new packages * Fix typos * update to edition 2018 * Extending reqwest * fix mime request * fix post file * Fix download basic * Remove toml lines * add error handling * Fix partial * Improve coding in unique * Borken links fix * Fix retain example * merging master * Completed updating for async * https://docs.rs/reqwest/0.10.6/reqwest/ references clients and they provided 404 errors * replace anyhow with error-chain * spelling and links Co-authored-by: pollosp <pollo.es.pollo@gmail.com> Co-authored-by: Olopez <pollosp@users.noreply.github.com>
1 parent b61c8e5 commit dd4efa8

File tree

17 files changed

+388
-321
lines changed

17 files changed

+388
-321
lines changed

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
[package]
22
name = "rust-cookbook"
3-
version = "0.1.0"
4-
authors = ["Brian Anderson <banderson@mozilla.com>"]
3+
version = "1.0.0"
4+
authors = ["Brian Anderson <banderson@mozilla.com>", "Andrew Gauger <andygauge@gmail.com>"]
55
edition = "2018"
66
license = "MIT/Apache-2.0"
77
publish = false
8-
98
build = "build.rs"
109

1110
[dependencies]
@@ -42,19 +41,20 @@ rand = "0.7.3"
4241
rand_distr = "0.2.2"
4342
rayon = "1.0"
4443
regex = "1.0"
45-
reqwest = "0.9"
44+
reqwest = { version = "0.10", features = ["blocking", "json", "stream"] }
4645
ring = "0.16.11"
4746
rusqlite = { version = "0.22", features = ["chrono"] }
4847
same-file = "1.0"
4948
select = "0.4"
5049
semver = "0.9"
51-
serde = "1.0"
50+
serde = { version = "1.0", features = ["derive"] }
5251
serde_derive = "1.0"
5352
serde_json = "1.0"
5453
tar = "0.4.12"
5554
tempfile = "3.1"
5655
threadpool = "1.6"
5756
toml = "0.4"
57+
tokio = { version = "0.2", features = ["full"] }
5858
unicode-segmentation = "1.2.1"
5959
url = "2.1"
6060
walkdir = "2.0"

src/development_tools/errors.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/errors/handle/retain.md

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The [error-chain] crate makes [matching] on different error types returned by
66
a function possible and relatively compact. [`ErrorKind`] determines the error
77
type.
88

9-
Uses [reqwest] to query a random integer generator web service. Converts
9+
Uses [reqwest]::[blocking] to query a random integer generator web service. Converts
1010
the string response into an integer. The Rust standard library,
1111
[reqwest], and the web service can all generate errors. Well defined Rust errors
1212
use [`foreign_links`]. An additional [`ErrorKind`] variant for the web service
@@ -15,51 +15,46 @@ error uses `errors` block of the `error_chain!` macro.
1515
```rust,edition2018
1616
use error_chain::error_chain;
1717
18-
use std::io::Read;
19-
2018
error_chain! {
2119
foreign_links {
2220
Io(std::io::Error);
2321
Reqwest(reqwest::Error);
2422
ParseIntError(std::num::ParseIntError);
2523
}
26-
2724
errors { RandomResponseError(t: String) }
2825
}
2926
30-
fn parse_response(mut response: reqwest::Response) -> Result<u32> {
31-
let mut body = String::new();
32-
response.read_to_string(&mut body)?;
33-
body.pop();
34-
body.parse::<u32>()
35-
.chain_err(|| ErrorKind::RandomResponseError(body))
27+
fn parse_response(response: reqwest::blocking::Response) -> Result<u32> {
28+
let mut body = response.text()?;
29+
body.pop();
30+
body
31+
.parse::<u32>()
32+
.chain_err(|| ErrorKind::RandomResponseError(body))
3633
}
3734
3835
fn run() -> Result<()> {
39-
let url =
40-
format!("https://www.random.org/integers/?num=1&min=0&max=10&col=1&base=10&format=plain");
41-
let response = reqwest::get(&url)?;
42-
let random_value: u32 = parse_response(response)?;
43-
44-
println!("a random number between 0 and 10: {}", random_value);
45-
46-
Ok(())
36+
let url =
37+
format!("https://www.random.org/integers/?num=1&min=0&max=10&col=1&base=10&format=plain");
38+
let response = reqwest::blocking::get(&url)?;
39+
let random_value: u32 = parse_response(response)?;
40+
println!("a random number between 0 and 10: {}", random_value);
41+
Ok(())
4742
}
4843
4944
fn main() {
50-
if let Err(error) = run() {
51-
match *error.kind() {
52-
ErrorKind::Io(_) => println!("Standard IO error: {:?}", error),
53-
ErrorKind::Reqwest(_) => println!("Reqwest error: {:?}", error),
54-
ErrorKind::ParseIntError(_) => println!("Standard parse int error: {:?}", error),
55-
ErrorKind::RandomResponseError(_) => println!("User defined error: {:?}", error),
56-
_ => println!("Other error: {:?}", error),
57-
}
45+
if let Err(error) = run() {
46+
match *error.kind() {
47+
ErrorKind::Io(_) => println!("Standard IO error: {:?}", error),
48+
ErrorKind::Reqwest(_) => println!("Reqwest error: {:?}", error),
49+
ErrorKind::ParseIntError(_) => println!("Standard parse int error: {:?}", error),
50+
ErrorKind::RandomResponseError(_) => println!("User defined error: {:?}", error),
51+
_ => println!("Other error: {:?}", error),
5852
}
53+
}
5954
}
6055
```
6156

6257
[`ErrorKind`]: https://docs.rs/error-chain/*/error_chain/example_generated/enum.ErrorKind.html
6358
[`foreign_links`]: https://docs.rs/error-chain/*/error_chain/#foreign-links
64-
59+
[blocking]: https://docs.rs/reqwest/*/reqwest/blocking/index.html
6560
[Matching]:https://docs.rs/error-chain/*/error_chain/#matching-errors

src/web.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,18 @@
2626
| [Get MIME type from filename][ex-mime-from-filename] | [![mime-badge]][mime] | [![cat-encoding-badge]][cat-encoding] |
2727
| [Parse the MIME type of a HTTP response][ex-http-response-mime-type] | [![mime-badge]][mime] [![reqwest-badge]][reqwest] | [![cat-net-badge]][cat-net] [![cat-encoding-badge]][cat-encoding] |
2828

29+
## Clients
2930

30-
{{#include web/clients.md}}
31+
| Recipe | Crates | Categories |
32+
|--------|--------|------------|
33+
| [Make a HTTP GET request][ex-url-basic] | [![reqwest-badge]][reqwest] | [![cat-net-badge]][cat-net] |
34+
| [Query the GitHub API][ex-rest-get] | [![reqwest-badge]][reqwest] [![serde-badge]][serde] | [![cat-net-badge]][cat-net] [![cat-encoding-badge]][cat-encoding] |
35+
| [Check if an API resource exists][ex-rest-head] | [![reqwest-badge]][reqwest] | [![cat-net-badge]][cat-net] |
36+
| [Create and delete Gist with GitHub API][ex-rest-post] | [![reqwest-badge]][reqwest] [![serde-badge]][serde] | [![cat-net-badge]][cat-net] [![cat-encoding-badge]][cat-encoding] |
37+
| [Consume a paginated RESTful API][ex-paginated-api] | [![reqwest-badge]][reqwest] [![serde-badge]][serde] | [![cat-net-badge]][cat-net] [![cat-encoding-badge]][cat-encoding] |
38+
| [Download a file to a temporary directory][ex-url-download] | [![reqwest-badge]][reqwest] [![tempdir-badge]][tempdir] | [![cat-net-badge]][cat-net] [![cat-filesystem-badge]][cat-filesystem] |
39+
| [Make a partial download with HTTP range headers][ex-progress-with-range] | [![reqwest-badge]][reqwest] | [![cat-net-badge]][cat-net] |
40+
| [POST a file to paste-rs][ex-file-post] | [![reqwest-badge]][reqwest] | [![cat-net-badge]][cat-net] |
3141

3242
[ex-extract-links-webpage]: web/scraping.html#extract-all-links-from-a-webpage-html
3343
[ex-check-broken-links]: web/scraping.html#check-a-webpage-for-broken-links
@@ -43,4 +53,15 @@
4353
[ex-mime-from-filename]: web/mime.html#get-mime-type-from-filename
4454
[ex-http-response-mime-type]: web/mime.html#parse-the-mime-type-of-a-http-response
4555

56+
[ex-url-basic]: web/clients/requests.html#make-a-http-get-request
57+
[ex-rest-custom-params]: web/clients/requests.html#set-custom-headers-and-url-parameters-for-a-rest-request
58+
[ex-rest-get]: web/clients/apis.html#query-the-github-api
59+
[ex-rest-head]: web/clients/apis.html#check-if-an-api-resource-exists
60+
[ex-rest-post]: web/clients/apis.html#create-and-delete-gist-with-github-api
61+
[ex-paginated-api]: web/clients/apis.html#consume-a-paginated-restful-api
62+
[ex-handle-rate-limited-api]: web/clients/apis.html#handle-a-rate-limited-api
63+
[ex-url-download]: web/clients/download.html#download-a-file-to-a-temporary-directory
64+
[ex-progress-with-range]: web/clients/download.html#make-a-partial-download-with-http-range-headers
65+
[ex-file-post]: web/clients/download.html#post-a-file-to-paste-rs
66+
4667
{{#include links.md}}

src/web/clients.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111
| [Make a partial download with HTTP range headers][ex-progress-with-range] | [![reqwest-badge]][reqwest] | [![cat-net-badge]][cat-net] |
1212
| [POST a file to paste-rs][ex-file-post] | [![reqwest-badge]][reqwest] | [![cat-net-badge]][cat-net] |
1313

14-
[ex-url-basic]: web/clients/requests.html#make-a-http-get-request
15-
[ex-rest-custom-params]: web/clients/requests.html#set-custom-headers-and-url-parameters-for-a-rest-request
16-
[ex-rest-get]: web/clients/apis.html#query-the-github-api
17-
[ex-rest-head]: web/clients/apis.html#check-if-an-api-resource-exists
18-
[ex-rest-post]: web/clients/apis.html#create-and-delete-gist-with-github-api
19-
[ex-paginated-api]: web/clients/apis.html#consume-a-paginated-restful-api
20-
[ex-handle-rate-limited-api]: web/clients/apis.html#handle-a-rate-limited-api
21-
[ex-url-download]: web/clients/download.html#download-a-file-to-a-temporary-directory
22-
[ex-progress-with-range]: web/clients/download.html#make-a-partial-download-with-http-range-headers
23-
[ex-file-post]: web/clients/download.html#post-a-file-to-paste-rs
14+
[ex-url-basic]: clients/requests.html#make-a-http-get-request
15+
[ex-rest-custom-params]: clients/requests.html#set-custom-headers-and-url-parameters-for-a-rest-request
16+
[ex-rest-get]: clients/apis.html#query-the-github-api
17+
[ex-rest-head]: clients/apis.html#check-if-an-api-resource-exists
18+
[ex-rest-post]: clients/apis.html#create-and-delete-gist-with-github-api
19+
[ex-paginated-api]: clients/apis.html#consume-a-paginated-restful-api
20+
[ex-handle-rate-limited-api]: clients/apis.html#handle-a-rate-limited-api
21+
[ex-url-download]: clients/download.html#download-a-file-to-a-temporary-directory
22+
[ex-progress-with-range]: clients/download.html#make-a-partial-download-with-http-range-headers
23+
[ex-file-post]: clients/download.html#post-a-file-to-paste-rs
2424

2525
{{#include ../links.md}}

src/web/clients/api/paginated.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ fetches the next page of results from the remote server as it arrives at the end
77
of each page.
88

99
```rust,edition2018,no_run
10+
use reqwest::Result;
1011
use serde::Deserialize;
11-
use reqwest::Error;
1212
1313
#[derive(Deserialize)]
1414
struct ApiResponse {
@@ -29,25 +29,25 @@ struct Meta {
2929
struct ReverseDependencies {
3030
crate_id: String,
3131
dependencies: <Vec<Dependency> as IntoIterator>::IntoIter,
32-
client: reqwest::Client,
32+
client: reqwest::blocking::Client,
3333
page: u32,
3434
per_page: u32,
3535
total: u32,
3636
}
3737
3838
impl ReverseDependencies {
39-
fn of(crate_id: &str) -> Result<Self, Error> {
39+
fn of(crate_id: &str) -> Result<Self> {
4040
Ok(ReverseDependencies {
4141
crate_id: crate_id.to_owned(),
4242
dependencies: vec![].into_iter(),
43-
client: reqwest::Client::new(),
43+
client: reqwest::blocking::Client::new(),
4444
page: 0,
4545
per_page: 100,
4646
total: 0,
4747
})
4848
}
4949
50-
fn try_next(&mut self) -> Result<Option<Dependency>, Error> {
50+
fn try_next(&mut self) -> Result<Option<Dependency>> {
5151
if let Some(dep) = self.dependencies.next() {
5252
return Ok(Some(dep));
5353
}
@@ -70,7 +70,7 @@ impl ReverseDependencies {
7070
}
7171
7272
impl Iterator for ReverseDependencies {
73-
type Item = Result<Dependency, Error>;
73+
type Item = Result<Dependency>;
7474
7575
fn next(&mut self) -> Option<Self::Item> {
7676
match self.try_next() {
@@ -81,7 +81,7 @@ impl Iterator for ReverseDependencies {
8181
}
8282
}
8383
84-
fn main() -> Result<(), Error> {
84+
fn main() -> Result<()> {
8585
for dep in ReverseDependencies::of("serde")? {
8686
println!("reverse dependency: {}", dep?.crate_id);
8787
}

src/web/clients/api/rest-get.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
[![reqwest-badge]][reqwest] [![serde-badge]][serde] [![cat-net-badge]][cat-net] [![cat-encoding-badge]][cat-encoding]
44

55
Queries GitHub [stargazers API v3](https://developer.github.com/v3/activity/starring/#list-stargazers)
6-
with [`reqwest::get`] to get list of all users who have marked a GitHub project with a star. [`reqwest::Response`] is deserialized with [`Response::json`] into `User` objects implementing [`serde::Deserialize`].
6+
with [`reqwest::get`] to get list of all users who have marked a GitHub project with a star.
7+
[`reqwest::Response`] is deserialized with [`Response::json`] into `User` objects implementing [`serde::Deserialize`].
8+
9+
[tokio::main] is used to set up the async executor and the process waits for [`reqwet::get`] to complete before
10+
processing the response into User instances.
711

812
```rust,edition2018,no_run
913
use serde::Deserialize;
@@ -15,14 +19,15 @@ struct User {
1519
id: u32,
1620
}
1721
18-
fn main() -> Result<(), Error> {
22+
#[tokio::main]
23+
async fn main() -> Result<(), Error> {
1924
let request_url = format!("https://api.github.com/repos/{owner}/{repo}/stargazers",
2025
owner = "rust-lang-nursery",
2126
repo = "rust-cookbook");
2227
println!("{}", request_url);
23-
let mut response = reqwest::get(&request_url)?;
28+
let response = reqwest::get(&request_url).await?;
2429
25-
let users: Vec<User> = response.json()?;
30+
let users: Vec<User> = response.json().await?;
2631
println!("{:?}", users);
2732
Ok(())
2833
}

src/web/clients/api/rest-head.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,23 @@ success. This is a quick way to query a rest resource without needing to receive
88
a body. [`reqwest::Client`] cofigured with [`ClientBuilder::timeout`] ensures
99
a request will not last longer than a timeout.
1010

11-
```rust,edition2018,no_run
11+
Due to both [`ClientBuilder::build`] and [`ReqwestBuilder::send`] returning [`reqwest::Error`]
12+
types, the shortcut [`reqwest::Result`] is used for the main function return type.
1213

13-
use reqwest::Error;
14+
```rust,edition2018,no_run
15+
use reqwest::Result;
1416
use std::time::Duration;
1517
use reqwest::ClientBuilder;
1618
17-
18-
fn main() -> Result<(), Error> {
19+
#[tokio::main]
20+
async fn main() -> Result<()> {
1921
let user = "ferris-the-crab";
2022
let request_url = format!("https://api.github.com/users/{}", user);
2123
println!("{}", request_url);
2224
2325
let timeout = Duration::new(5, 0);
2426
let client = ClientBuilder::new().timeout(timeout).build()?;
25-
let response = client.head(&request_url).send()?;
27+
let response = client.head(&request_url).send().await?;
2628
2729
if response.status().is_success() {
2830
println!("{} is a user!", user);
@@ -34,6 +36,10 @@ fn main() -> Result<(), Error> {
3436
}
3537
```
3638

39+
[`ClientBuilder::build`]: https://docs.rs/reqwest/*/reqwest/struct.ClientBuilder.html#method.build
3740
[`Client::head`]: https://docs.rs/reqwest/*/reqwest/struct.Client.html#method.head
3841
[`ClientBuilder::timeout`]: https://docs.rs/reqwest/*/reqwest/struct.ClientBuilder.html#method.timeout
42+
[`RequestBuilder::send`]: https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html#method.send
3943
[`reqwest::Client`]: https://docs.rs/reqwest/*/reqwest/struct.Client.html
44+
[`reqwest::Error`]: https://docs.rs/reqwest/*/reqwest/struct.Error.html
45+
[`reqwest::Result`]:https://docs.rs/reqwest/*/reqwest/type.Result.html

src/web/clients/api/rest-post.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,27 @@ body. [`RequestBuilder::basic_auth`] handles authentication. The call to
1212
[`RequestBuilder::send`] synchronously executes the requests.
1313

1414
```rust,edition2018,no_run
15-
# use error_chain::error_chain;
15+
use error_chain::error_chain;
1616
use serde::Deserialize;
17-
use serde_json::json;
18-
17+
use serde_json::json;
1918
use std::env;
2019
use reqwest::Client;
21-
#
22-
# error_chain! {
23-
# foreign_links {
24-
# EnvVar(env::VarError);
25-
# HttpRequest(reqwest::Error);
26-
# }
27-
# }
20+
21+
error_chain! {
22+
foreign_links {
23+
EnvVar(env::VarError);
24+
HttpRequest(reqwest::Error);
25+
}
26+
}
2827
2928
#[derive(Deserialize, Debug)]
3029
struct Gist {
3130
id: String,
3231
html_url: String,
3332
}
3433
35-
fn main() -> Result<()> {
34+
#[tokio::main]
35+
async fn main() -> Result<()> {
3636
let gh_user = env::var("GH_USER")?;
3737
let gh_pass = env::var("GH_PASS")?;
3838
@@ -46,20 +46,20 @@ fn main() -> Result<()> {
4646
}});
4747
4848
let request_url = "https://api.github.com/gists";
49-
let mut response = Client::new()
49+
let response = Client::new()
5050
.post(request_url)
5151
.basic_auth(gh_user.clone(), Some(gh_pass.clone()))
5252
.json(&gist_body)
53-
.send()?;
53+
.send().await?;
5454
55-
let gist: Gist = response.json()?;
55+
let gist: Gist = response.json().await?;
5656
println!("Created {:?}", gist);
5757
5858
let request_url = format!("{}/{}",request_url, gist.id);
5959
let response = Client::new()
6060
.delete(&request_url)
6161
.basic_auth(gh_user, Some(gh_pass))
62-
.send()?;
62+
.send().await?;
6363
6464
println!("Gist {} deleted! Status code: {}",gist.id, response.status());
6565
Ok(())

0 commit comments

Comments
 (0)