Skip to main content

twapi_oauth2/
lib.rs

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            // ジッターとエクスポーネンシャルバックオフを組み合わせる
40            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            // ジッターとエクスポーネンシャルバックオフを組み合わせる
72            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}