Skip to content

Commit c41af38

Browse files
codeworm96gaocegege
authored andcommitted
Add paging handling (#11)
* Add paging handling * format * Refactor & make it builds under stable
1 parent 57e1c28 commit c41af38

File tree

1 file changed

+73
-16
lines changed

1 file changed

+73
-16
lines changed

src/github.rs

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,64 @@
11
use json;
22
use reqwest;
3-
use reqwest::header::{qitem, Accept, Authorization, Headers, UserAgent};
3+
use reqwest::header::{qitem, Accept, Authorization, Headers, Link, RelationType, UserAgent};
44
use reqwest::mime::Mime;
55
use reqwest::Client;
66

77
use std::io::Read;
8+
use std::mem;
89

910
use error::Error;
1011

1112
const API_ROOT: &'static str = "https://api.github.com";
1213

13-
pub fn fetch<'a>(
14-
repo: &str,
15-
issue: &str,
16-
key: Option<&str>,
17-
) -> Result<impl Iterator<Item = String>, Error> {
18-
let client = Client::new();
19-
let url = format!("{}/repos/{}/issues/{}/comments", API_ROOT, repo, issue);
14+
pub struct Comments<'a> {
15+
client: Client,
16+
key: Option<&'a str>,
17+
comments: Box<Iterator<Item = String>>,
18+
next: Option<String>,
19+
}
20+
21+
impl<'a> Iterator for Comments<'a> {
22+
type Item = String;
23+
24+
fn next(&mut self) -> Option<String> {
25+
loop {
26+
if let Some(s) = self.comments.next() {
27+
return Some(s);
28+
} else {
29+
if let Some(url) = mem::replace(&mut self.next, None) {
30+
if let Ok(page) = fetch_page(&self.client, &url, self.key) {
31+
self.comments = page.comments;
32+
self.next = page.next;
33+
} else {
34+
return None;
35+
}
36+
} else {
37+
return None;
38+
}
39+
}
40+
}
41+
}
42+
}
43+
44+
struct Page {
45+
comments: Box<Iterator<Item = String>>,
46+
next: Option<String>,
47+
}
2048

49+
fn next_link(headers: &Headers) -> Option<&str> {
50+
let link: &Link = headers.get()?;
51+
for v in link.values() {
52+
if let Some(rel) = v.rel() {
53+
if rel.contains(&RelationType::Next) {
54+
return Some(v.link());
55+
}
56+
}
57+
}
58+
None
59+
}
60+
61+
fn fetch_page(client: &Client, url: &str, key: Option<&str>) -> Result<Page, Error> {
2162
let mut headers = Headers::new();
2263
let accept_mime: Mime = "application/vnd.github.v3+json".parse().unwrap();
2364
headers.set(Accept(vec![qitem(accept_mime)]));
@@ -27,22 +68,38 @@ pub fn fetch<'a>(
2768
None => {}
2869
}
2970

30-
let mut res = client.get(&url).headers(headers).send()?;
71+
let mut res = client.get(url).headers(headers).send()?;
3172

3273
if res.status() != reqwest::StatusCode::Ok {
3374
Err(Error::FetchErr(res))
3475
} else {
3576
let mut content = String::new();
3677
res.read_to_string(&mut content)?;
37-
let comments = json::parse(&content)?;
38-
match comments {
39-
json::JsonValue::Array(cs) => {
40-
Ok(cs.into_iter().flat_map(|mut c| match c["body"].take() {
78+
let content = json::parse(&content)?;
79+
let comments = match content {
80+
json::JsonValue::Array(cs) => Ok(Box::new(cs.into_iter().flat_map(|mut c| {
81+
match c["body"].take() {
4182
json::JsonValue::String(s) => Some(s),
4283
_ => None,
43-
}))
44-
}
84+
}
85+
}))),
4586
_ => Err(Error::JsonParseErr),
46-
}
87+
}?;
88+
Ok(Page {
89+
comments: comments,
90+
next: next_link(res.headers()).map(|s| s.to_owned()),
91+
})
4792
}
4893
}
94+
95+
pub fn fetch<'a>(repo: &str, issue: &str, key: Option<&'a str>) -> Result<Comments<'a>, Error> {
96+
let client = Client::new();
97+
let url = format!("{}/repos/{}/issues/{}/comments", API_ROOT, repo, issue);
98+
let page = fetch_page(&client, &url, key)?;
99+
Ok(Comments {
100+
client: client,
101+
key: key,
102+
comments: page.comments,
103+
next: page.next,
104+
})
105+
}

0 commit comments

Comments
 (0)