mirror of
https://github.com/rust-lang-nursery/rust-cookbook
synced 2025-02-16 20:28:27 +00:00
Real error handling for paginated iterator example
This commit is contained in:
parent
5974bee3c2
commit
de913078f3
1 changed files with 60 additions and 63 deletions
123
src/net.md
123
src/net.md
|
@ -535,16 +535,19 @@ fn run() -> Result<()> {
|
||||||
|
|
||||||
[![reqwest-badge]][reqwest] [![serde-badge]][serde] [![cat-net-badge]][cat-net] [![cat-encoding-badge]][cat-encoding]
|
[![reqwest-badge]][reqwest] [![serde-badge]][serde] [![cat-net-badge]][cat-net] [![cat-encoding-badge]][cat-encoding]
|
||||||
|
|
||||||
In this example, A Paginated JSON API is exposed as a Rust Iterator, and will create further requests until the iteration is finished.
|
Wraps a paginated web API in a convenient Rust iterator. The iterator lazily
|
||||||
|
fetches the next page of results from the remote server as it arrives at the end
|
||||||
|
of each page.
|
||||||
|
|
||||||
An initial request is made with [`reqwest::get`] and that is used to create a `DependencyList`. The `DependencyList` implements the [`std::iter::Iterator`] trait so it can be consumed in a rust fashion.
|
```rust
|
||||||
|
|
||||||
```rust, no_run
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
extern crate serde;
|
||||||
extern crate reqwest;
|
extern crate reqwest;
|
||||||
|
|
||||||
use std::vec::IntoIter;
|
#[macro_use]
|
||||||
|
extern crate error_chain;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct ApiResponse {
|
struct ApiResponse {
|
||||||
|
@ -552,7 +555,7 @@ struct ApiResponse {
|
||||||
meta: Meta,
|
meta: Meta,
|
||||||
}
|
}
|
||||||
|
|
||||||
//Could include more fields here if needed
|
// Could capture more fields here if needed
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Dependency {
|
struct Dependency {
|
||||||
crate_id: String,
|
crate_id: String,
|
||||||
|
@ -563,82 +566,76 @@ struct Meta {
|
||||||
total: u32
|
total: u32
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DependencyList {
|
struct ReverseDependencies {
|
||||||
crate_id: String,
|
crate_id: String,
|
||||||
dependencies: IntoIter<Dependency>,
|
dependencies: <Vec<Dependency> as IntoIterator>::IntoIter,
|
||||||
page: u32,
|
page: u32,
|
||||||
per_page: u32,
|
per_page: u32,
|
||||||
total_amount: u32
|
total: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DependencyList {
|
error_chain! {
|
||||||
|
foreign_links {
|
||||||
|
Reqwest(reqwest::Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn new(crate_id: &str) -> DependencyList {
|
impl ReverseDependencies {
|
||||||
|
fn of(crate_id: &str) -> Self {
|
||||||
let page = 1;
|
ReverseDependencies {
|
||||||
let per_page = 100;
|
crate_id: crate_id.to_owned(),
|
||||||
let api_response = DependencyList::get_api_response(crate_id, page, per_page).expect("Could not get API response!");
|
dependencies: vec![].into_iter(),
|
||||||
let total_amount = api_response.meta.total;
|
page: 0,
|
||||||
|
per_page: 100,
|
||||||
DependencyList {
|
total: 0,
|
||||||
crate_id: String::from(crate_id),
|
|
||||||
dependencies: api_response.dependencies.into_iter(),
|
|
||||||
page: page,
|
|
||||||
per_page: per_page,
|
|
||||||
total_amount: total_amount
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_api_response(crate_id: &str, page: u32, per_page: u32) -> Result<ApiResponse, reqwest::Error> {
|
fn try_next(&mut self) -> Result<Option<Dependency>> {
|
||||||
let request_url = format!("https://crates.io/api/v1/crates/{}/reverse_dependencies?page={}&per_page={}", crate_id, page, per_page);
|
// If the previous page has a dependency that hasn't been looked at.
|
||||||
reqwest::get(&request_url)?.json::<ApiResponse>()
|
if let Some(dep) = self.dependencies.next() {
|
||||||
}
|
return Ok(Some(dep));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no more reverse dependencies.
|
||||||
|
if self.page > 0 && self.page * self.per_page >= self.total {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the next page.
|
||||||
|
self.page += 1;
|
||||||
|
let url = format!("https://crates.io/api/v1/crates/{}/reverse_dependencies?page={}&per_page={}",
|
||||||
|
self.crate_id, self.page, self.per_page);
|
||||||
|
let response = reqwest::get(&url)?.json::<ApiResponse>()?;
|
||||||
|
self.dependencies = response.dependencies.into_iter();
|
||||||
|
self.total = response.meta.total;
|
||||||
|
Ok(self.dependencies.next())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for DependencyList {
|
impl Iterator for ReverseDependencies {
|
||||||
type Item = Dependency;
|
type Item = Result<Dependency>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Dependency> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
match self.dependencies.next() {
|
// Some juggling required here because `try_next` returns a result
|
||||||
Some(dependency) => {
|
// containing an option, while `next` is supposed to return an option
|
||||||
Some(dependency)
|
// containing a result.
|
||||||
},
|
match self.try_next() {
|
||||||
None => {
|
Ok(Some(dep)) => Some(Ok(dep)),
|
||||||
|
Ok(None) => None,
|
||||||
//If we're not at the end we request the next page
|
Err(err) => Some(Err(err)),
|
||||||
if self.total_amount > self.page * self.per_page {
|
|
||||||
|
|
||||||
self.page += 1;
|
|
||||||
|
|
||||||
let api_response = DependencyList::get_api_response(&self.crate_id, self.page, self.per_page).expect("Could not get API response!");
|
|
||||||
|
|
||||||
self.dependencies = api_response.dependencies.into_iter();
|
|
||||||
|
|
||||||
//Send the next dependency
|
|
||||||
self.dependencies.next()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
None
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run() -> Result<()> {
|
||||||
fn main() {
|
for dep in ReverseDependencies::of("serde") {
|
||||||
let dependency_list = DependencyList::new("serde");
|
println!("reverse dependency: {}", dep?.crate_id);
|
||||||
|
|
||||||
for dependency in dependency_list {
|
|
||||||
println!("Dependency: {}", dependency.crate_id);
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quick_main!(run);
|
||||||
```
|
```
|
||||||
|
|
||||||
[ex-file-post]: #ex-file-post
|
[ex-file-post]: #ex-file-post
|
||||||
|
|
Loading…
Add table
Reference in a new issue