11use json;
22use reqwest;
3- use reqwest:: header:: { qitem, Accept , Authorization , Headers , UserAgent } ;
3+ use reqwest:: header:: { qitem, Accept , Authorization , Headers , Link , RelationType , UserAgent } ;
44use reqwest:: mime:: Mime ;
55use reqwest:: Client ;
66
77use std:: io:: Read ;
8+ use std:: mem;
89
910use error:: Error ;
1011
1112const 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