github_star_counter/
request.rs1use super::Error;
2use log::{error, info};
3use serde::{de::DeserializeOwned, Deserialize};
4use std::ops::AddAssign;
5use std::sync::atomic::{AtomicU64, Ordering};
6use std::sync::Mutex;
7use std::time::Duration;
8
9lazy_static! {
10 pub static ref TOTAL_DURATION: Mutex<Duration> = Mutex::new(Duration::default());
11 pub static ref TOTAL_BYTES_RECEIVED_IN_BODY: AtomicU64 = AtomicU64::default();
12}
13
14#[derive(Clone)]
15pub struct BasicAuth {
16 pub username: String,
17 pub password: Option<String>,
18}
19
20impl ToString for BasicAuth {
21 fn to_string(&self) -> String {
22 format!(
23 "Basic {}",
24 base64::encode(&match &self.password {
25 Some(password) => format!("{}:{}", self.username, password),
26 None => self.username.clone(),
27 })
28 )
29 }
30}
31
32pub async fn json_log_failure<D>(url: String, auth: Option<BasicAuth>) -> Result<D, Error>
33where
35 D: DeserializeOwned,
36{
37 match json(url, auth).await {
38 Ok(v) => Ok(v),
39 Err(e) => {
40 error!("{}", e);
41 Err(e)
42 }
43 }
44}
45
46pub async fn json<D>(url: String, auth: Option<BasicAuth>) -> Result<D, Error>
48where
50 D: DeserializeOwned,
51{
52 let url = format!("https://api.github.com/{}", url);
53 let mut req = surf::get(&url);
54 req = req.set_header("User-Agent", "GitHub StarCounter.rs");
55 if let Some(auth) = auth {
56 req = req.set_header("Authorization", auth.to_string());
57 }
58 info!("{} - requested", url);
59 let started = std::time::Instant::now();
60 let mut res = req.await.map_err(|e| e.to_string())?;
61 let status = res.status();
62 let bytes = res.body_bytes().await?;
63 let elapsed = started.elapsed();
64 info!(
65 "{} - received in {:?} ({})",
66 url,
67 elapsed,
68 bytesize::ByteSize(bytes.len() as u64)
69 );
70 TOTAL_DURATION.lock().unwrap().add_assign(elapsed);
71 TOTAL_BYTES_RECEIVED_IN_BODY.fetch_add(bytes.len() as u64, Ordering::Relaxed);
72
73 if status.is_success() {
74 Ok(serde_json::from_slice(&bytes)?)
75 } else {
76 #[derive(Deserialize)]
77 struct Error {
78 message: String,
79 }
80 let err: Error = serde_json::from_slice(&bytes).or_else(|e| {
81 Ok::<_, serde_json::Error>(Error {
82 message: format!(
83 "Unexpected error message format returned by Github: '{:#?}'",
84 e
85 ),
86 })
87 })?;
88 Err(err.message.into())
89 }
90}