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
17pub(crate) async fn execute_retry<T>(
18    f: impl Fn() -> RequestBuilder,
19    try_count: usize,
20    retry_millis: u64,
21) -> Result<(T, StatusCode, HeaderMap), Error>
22where
23    T: serde::de::DeserializeOwned,
24{
25    for i in 0..try_count {
26        let req = f();
27        let res = req.send().await?;
28        let status = res.status();
29        let headers = res.headers().clone();
30        if status.is_success() {
31            let json: T = res.json().await?;
32            return Ok((json, status, headers));
33        } else if status.is_client_error() {
34            let body = res.text().await.unwrap_or_default();
35            return Err(Error::ClientError(body, status, headers));
36        }
37        if i + 1 < try_count {
38            // ジッターとエクスポーネンシャルバックオフを組み合わせる
39            let jitter: u64 = rand::random::<u64>() % retry_millis;
40            let exp_backoff = 2u64.pow(i as u32) * retry_millis;
41            let retry_duration = Duration::from_millis(exp_backoff + jitter);
42            tokio::time::sleep(retry_duration).await;
43        } else {
44            let body = res.text().await.unwrap_or_default();
45            return Err(Error::RetryOver(body, status, headers));
46        }
47    }
48    unreachable!()
49}
50
51pub(crate) async fn execute_retry_body(
52    f: impl Fn() -> RequestBuilder,
53    try_count: usize,
54    retry_millis: u64,
55) -> Result<(String, StatusCode, HeaderMap), Error>
56{
57    for i in 0..try_count {
58        let req = f();
59        let res = req.send().await?;
60        let status = res.status();
61        let headers = res.headers().clone();
62        if status.is_success() {
63            let body = res.text().await?;
64            return Ok((body, status, headers));
65        } else if status.is_client_error() {
66            let body = res.text().await.unwrap_or_default();
67            return Err(Error::ClientError(body, status, headers));
68        }
69        if i + 1 < try_count {
70            // ジッターとエクスポーネンシャルバックオフを組み合わせる
71            let jitter: u64 = rand::random::<u64>() % retry_millis;
72            let exp_backoff = 2u64.pow(i as u32) * retry_millis;
73            let retry_duration = Duration::from_millis(exp_backoff + jitter);
74            tokio::time::sleep(retry_duration).await;
75        } else {
76            let body = res.text().await.unwrap_or_default();
77            return Err(Error::RetryOver(body, status, headers));
78        }
79    }
80    unreachable!()
81}