1use std::time::Duration;
2
3use reqwest::{RequestBuilder, StatusCode, header::HeaderMap};
4
5pub mod error;
6
7#[cfg(feature = "oauth1a")]
8pub mod oauth1a;
9
10#[cfg(feature = "oauth2")]
11pub mod oauth2;
12
13pub use reqwest;
14
15use crate::error::Error;
16
17#[allow(dead_code)]
18pub(crate) async fn execute_retry<T>(
19 f: impl Fn() -> RequestBuilder,
20 try_count: usize,
21 retry_duration: Duration,
22) -> Result<(T, StatusCode, HeaderMap), Error>
23where
24 T: serde::de::DeserializeOwned,
25{
26 for i in 0..try_count {
27 let req = f();
28 let res = req.send().await?;
29 let status = res.status();
30 let headers = res.headers().clone();
31 if status.is_success() {
32 let json: T = res.json().await?;
33 return Ok((json, status, headers));
34 } else if status.is_client_error() {
35 let body = res.text().await.unwrap_or_default();
36 return Err(Error::ClientError(body, status, headers));
37 }
38 if i + 1 < try_count {
39 let jitter: u64 = rand::random::<u64>() % retry_duration.as_millis() as u64;
41 let exp_backoff = 2u64.pow(i as u32) * retry_duration.as_millis() as u64;
42 let retry_duration = Duration::from_millis(exp_backoff + jitter);
43 tokio::time::sleep(retry_duration).await;
44 } else {
45 let body = res.text().await.unwrap_or_default();
46 return Err(Error::RetryOver(body, status, headers));
47 }
48 }
49 unreachable!()
50}
51
52#[allow(dead_code)]
53pub(crate) async fn execute_retry_body(
54 f: impl Fn() -> RequestBuilder,
55 try_count: usize,
56 retry_duration: Duration,
57) -> Result<(String, StatusCode, HeaderMap), Error> {
58 for i in 0..try_count {
59 let req = f();
60 let res = req.send().await?;
61 let status = res.status();
62 let headers = res.headers().clone();
63 if status.is_success() {
64 let body = res.text().await?;
65 return Ok((body, status, headers));
66 } else if status.is_client_error() {
67 let body = res.text().await.unwrap_or_default();
68 return Err(Error::ClientError(body, status, headers));
69 }
70 if i + 1 < try_count {
71 let jitter: u64 = rand::random::<u64>() % retry_duration.as_millis() as u64;
73 let exp_backoff = 2u64.pow(i as u32) * retry_duration.as_millis() as u64;
74 let retry_duration = Duration::from_millis(exp_backoff + jitter);
75 tokio::time::sleep(retry_duration).await;
76 } else {
77 let body = res.text().await.unwrap_or_default();
78 return Err(Error::RetryOver(body, status, headers));
79 }
80 }
81 unreachable!()
82}
83
84pub(crate) fn make_url(base_url: &str, path: &str, prefix_url: &Option<String>) -> String {
85 if let Some(prefix_url) = prefix_url {
86 format!("{}{}", prefix_url, path)
87 } else {
88 format!("{}{}", base_url, path)
89 }
90}