hacker_news/client/
json_client.rs1use std::error::Error;
2use log;
3use serde_json;
4use reqwest;
5use reqwest::blocking::Client;
6use reqwest::blocking::Request;
7use reqwest::blocking::Response;
8use crate::error::HnError;
9use crate::error::HttpError;
10use crate::model::Id;
11use crate::model::firebase::User;
12use crate::model::firebase::Item;
13use crate::model::firebase::ItemsAndProfiles;
14
15pub struct JsonClient {
16 http_client: reqwest::blocking::Client,
17}
18
19const BASE_URL: &str = "https://hacker-news.firebaseio.com/v0";
20
21#[allow(clippy::new_without_default)]
22impl JsonClient {
23
24 pub fn new() -> Self {
25 Self {
26 http_client: Client::new(),
27 }
28 }
29
30 fn send(&self, req: Request) -> Result<Response, Box<dyn Error>> {
31 let resp = self.http_client.execute(req)?;
32 let status = resp.status().as_u16();
33 if status != 200 {
34 let err = HttpError {
35 url: resp.url().to_string(),
36 code: status,
37 };
38 log::error!("Recieved non 200 status: {:?}", err);
39 return Err(Box::new(HnError::HttpError(err)));
40 }
41 log::debug!("Recieved 200 status, response = {:?}", resp);
42
43 Ok(resp)
44 }
45
46 fn get_url(&self, url: &str) -> Result<Response, Box<dyn Error>> {
47 let req = self.http_client.get(url);
48 let resp = self.send(req.build()?)?;
49
50 Ok(resp)
51 }
52
53 pub fn item(&self, id: Id) -> Result <Item, Box<dyn Error>> {
54 let url = format!("{base_url}/item/{id}.json",
55 base_url=BASE_URL,
56 id=id
57 );
58
59 let resp = self.get_url(&url)?;
60 let text = resp.text()?;
61 log::debug!("text = {:?}", text);
62 let item: Item = serde_json::from_str(&text)?;
63 log::debug!("item = {:?}", item);
64
65 Ok(item)
66 }
67
68 pub fn max_item(&self) -> Result <Id, Box<dyn Error>> {
69 let url = format!("{base_url}/maxitem.json",
70 base_url=BASE_URL,
71 );
72
73 let resp = self.get_url(&url)?;
74 let text = resp.text()?;
75 log::debug!("text = {:?}", text);
76 let id: Id = serde_json::from_str(&text)?;
77 log::debug!("maxitem = {:?}", id);
78
79 Ok(id)
80 }
81
82 pub fn user(&self, username: String) -> Result<User, Box<dyn Error>> {
83 let url = format!("{base_url}/user/{id}.json",
84 base_url=BASE_URL,
85 id=username
86 );
87
88 let resp = self.get_url(&url)?;
89 let text = resp.text()?;
90 log::debug!("text = {:?}", text);
91 let user: User = serde_json::from_str(&text)?;
92 log::debug!("user = {:?}", user);
93
94 Ok(user)
95 }
96
97 pub fn new_stories(&self) -> Result<Vec<Id>, Box<dyn Error>> {
103 let url = format!("{base_url}/newstories.json",
104 base_url=BASE_URL,
105 );
106
107 let resp = self.get_url(&url)?;
108 let text = resp.text()?;
109 log::debug!("text = {:?}", text);
110 let ids: Vec<Id> = serde_json::from_str(&text)?;
111 log::debug!("ids = {:?}", ids);
112
113 Ok(ids)
114 }
115
116 pub fn top_stories(&self) -> Result<Vec<Id>, Box<dyn Error>> {
117 let url = format!("{base_url}/topstories.json",
118 base_url=BASE_URL,
119 );
120
121 let resp = self.get_url(&url)?;
122 let text = resp.text()?;
123 log::debug!("text = {:?}", text);
124 let ids: Vec<Id> = serde_json::from_str(&text)?;
125 log::debug!("ids = {:?}", ids);
126
127 Ok(ids)
128 }
129
130 pub fn updates(&self) -> Result<(Vec<Id>, Vec<String>), Box<dyn Error>> {
131 let url = format!("{base_url}/updates.json",
132 base_url=BASE_URL,
133 );
134
135 let resp = self.get_url(&url)?;
136 let text = resp.text()?;
137 log::debug!("text = {:?}", text);
138 let items_and_profiles: ItemsAndProfiles = serde_json::from_str(&text)?;
139 let items = items_and_profiles.items;
140 let profiles = items_and_profiles.profiles;
141 let updates = (items, profiles);
142 log::debug!("updates = {:?}", updates);
143
144 Ok(updates)
145 }
146
147 pub fn ask_stories(&self) -> Result<Vec<Id>, Box<dyn Error>> {
148 let url = format!("{base_url}/askstories.json",
149 base_url=BASE_URL,
150 );
151 let resp = self.get_url(&url)?;
152 let text = resp.text()?;
153 log::debug!("text = {:?}", text);
154 let ids: Vec<Id> = serde_json::from_str(&text)?;
155 log::debug!("ids = {:?}", ids);
156
157 Ok(ids)
158 }
159
160 pub fn show_stories(&self) -> Result<Vec<Id>, Box<dyn Error>> {
161 let url = format!("{base_url}/showstories.json",
162 base_url=BASE_URL,
163 );
164
165 let resp = self.get_url(&url)?;
166 let text = resp.text()?;
167 log::debug!("text = {:?}", text);
168 let ids: Vec<Id> = serde_json::from_str(&text)?;
169 log::debug!("ids = {:?}", ids);
170
171 Ok(ids)
172 }
173
174 pub fn job_stories(&self) -> Result<Vec<Id>, Box<dyn Error>> {
175 let url = format!("{base_url}/jobstories.json",
176 base_url=BASE_URL,
177 );
178
179 let resp = self.get_url(&url)?;
180 let text = resp.text()?;
181 log::debug!("text = {:?}", text);
182 let ids: Vec<Id> = serde_json::from_str(&text)?;
183 log::debug!("ids = {:?}", ids);
184
185 Ok(ids)
186 }
187
188}
189
190#[cfg(test)]
191mod tests {
192
193 use super::JsonClient;
194 use std::error::Error;
195 use crate::util::setup;
196
197 #[test]
198 fn test_item() -> Result<(), Box<dyn Error>> {
199 setup();
200
201 let id_story = 27476206;
202 let id_comment = 27509155;
203
204 let client = JsonClient::new();
205 let story = client.item(id_story)?;
206 log::debug!("item = {:?}", story);
207 assert!(story.is_story());
208
209 let comment = client.item(id_comment)?;
210 log::debug!("item = {:?}", comment);
211 assert!(comment.is_comment());
212
213 Ok(())
214 }
215
216 #[test]
217 fn test_max_item() -> Result<(), Box<dyn Error>> {
218 setup();
219
220 let client = JsonClient::new();
221 let item = client.max_item()?;
222 log::debug!("maxitem = {:?}", item);
223
224 Ok(())
225 }
226
227 #[test]
228 fn test_user() -> Result<(), Box<dyn Error>> {
229 setup();
230
231 let client = JsonClient::new();
232 let user = client.user("pg".to_string())?;
233 log::debug!("user = {:?}", user);
234
235 Ok(())
236 }
237
238 #[test]
239 fn test_new_stories() -> Result<(), Box<dyn Error>> {
240 setup();
241
242 let client = JsonClient::new();
243 let ids = client.new_stories()?;
244 log::debug!("ids = {:?}", ids);
245
246 Ok(())
247 }
248
249 #[test]
250 fn test_top_stories() -> Result<(), Box<dyn Error>> {
251 setup();
252
253 let client = JsonClient::new();
254 let ids = client.top_stories()?;
255 log::debug!("ids = {:?}", ids);
256
257 Ok(())
258 }
259
260 #[test]
261 fn test_updates() -> Result<(), Box<dyn Error>> {
262 setup();
263
264 let client = JsonClient::new();
265 let updates = client.updates()?;
266 log::debug!("updates = {:?}", updates);
267
268 Ok(())
269 }
270
271 #[test]
272 fn test_ask_stories() -> Result<(), Box<dyn Error>> {
273 setup();
274
275 let client = JsonClient::new();
276 let ids = client.ask_stories()?;
277 log::debug!("ids = {:?}", ids);
278
279 Ok(())
280 }
281
282 #[test]
283 fn test_show_stories() -> Result<(), Box<dyn Error>> {
284 setup();
285
286 let client = JsonClient::new();
287 let ids = client.show_stories()?;
288 log::debug!("ids = {:?}", ids);
289
290 Ok(())
291 }
292
293 #[test]
294 fn test_job_stories() -> Result<(), Box<dyn Error>> {
295 setup();
296
297 let client = JsonClient::new();
298 let ids = client.job_stories()?;
299 log::debug!("ids = {:?}", ids);
300
301 Ok(())
302 }
303}