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