Skip to content

Commit a2253d7

Browse files
committed
iteration instead of recursion
1 parent da72518 commit a2253d7

File tree

1 file changed

+63
-61
lines changed

1 file changed

+63
-61
lines changed

cpp-linter/src/rest_api/mod.rs

Lines changed: 63 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use std::time::Duration;
1212
// non-std crates
1313
use anyhow::{anyhow, Error, Result};
1414
use chrono::DateTime;
15-
use futures::future::{BoxFuture, FutureExt};
1615
use reqwest::header::{HeaderMap, HeaderValue};
1716
use reqwest::{Client, IntoUrl, Method, Request, Response, Url};
1817

@@ -111,85 +110,88 @@ pub trait RestApiClient {
111110
request: Request,
112111
rate_limit_headers: RestApiRateLimitHeaders,
113112
retries: u64,
114-
) -> BoxFuture<'static, Result<Response>> {
113+
) -> impl Future<Output = Result<Response>> + Send {
115114
async move {
116-
let result = client
117-
.execute(request.try_clone().ok_or(anyhow!(
118-
"Failed to clone request object for recursive behavior"
119-
))?)
120-
.await;
121-
if let Ok(response) = &result {
122-
if [403u16, 429u16].contains(&response.status().as_u16()) {
123-
// rate limit exceeded
124-
125-
// check if primary rate limit was violated; panic if so.
126-
let mut requests_remaining = None;
127-
if let Some(remaining) = response.headers().get(&rate_limit_headers.remaining) {
128-
if let Ok(count) = remaining.to_str() {
129-
if let Ok(value) = count.parse::<i64>() {
130-
requests_remaining = Some(value);
131-
} else {
132-
log::debug!(
115+
for i in retries..5 {
116+
let result = client
117+
.execute(request.try_clone().ok_or(anyhow!(
118+
"Failed to clone request object for recursive behavior"
119+
))?)
120+
.await;
121+
if let Ok(response) = &result {
122+
if [403u16, 429u16].contains(&response.status().as_u16()) {
123+
// rate limit may have been exceeded
124+
125+
// check if primary rate limit was violated; panic if so.
126+
let mut requests_remaining = None;
127+
if let Some(remaining) =
128+
response.headers().get(&rate_limit_headers.remaining)
129+
{
130+
if let Ok(count) = remaining.to_str() {
131+
if let Ok(value) = count.parse::<i64>() {
132+
requests_remaining = Some(value);
133+
} else {
134+
log::debug!(
133135
"Failed to parse i64 from remaining attempts about rate limit: {count}"
134136
);
137+
}
135138
}
139+
} else {
140+
// NOTE: I guess it is sometimes valid for a request to
141+
// not include remaining rate limit attempts
142+
log::debug!(
143+
"Response headers do not include remaining API usage count"
144+
);
136145
}
137-
} else {
138-
// NOTE: I guess it is sometimes valid for a request to
139-
// not include remaining rate limit attempts
140-
log::debug!("Response headers do not include remaining API usage count");
141-
}
142-
if requests_remaining.is_some_and(|v| v <= 0) {
143-
if let Some(reset_value) = response.headers().get(&rate_limit_headers.reset)
144-
{
145-
if let Ok(epoch) = reset_value.to_str() {
146-
if let Ok(value) = epoch.parse::<i64>() {
147-
if let Some(reset) = DateTime::from_timestamp(value, 0) {
148-
return Err(anyhow!(
149-
"REST API rate limit exceeded! Resets at {}",
150-
reset
151-
));
152-
}
153-
} else {
154-
log::debug!(
146+
if requests_remaining.is_some_and(|v| v <= 0) {
147+
if let Some(reset_value) =
148+
response.headers().get(&rate_limit_headers.reset)
149+
{
150+
if let Ok(epoch) = reset_value.to_str() {
151+
if let Ok(value) = epoch.parse::<i64>() {
152+
if let Some(reset) = DateTime::from_timestamp(value, 0) {
153+
return Err(anyhow!(
154+
"REST API rate limit exceeded! Resets at {}",
155+
reset
156+
));
157+
}
158+
} else {
159+
log::debug!(
155160
"Failed to parse i64 from reset time about rate limit: {epoch}"
156161
);
162+
}
157163
}
164+
} else {
165+
log::debug!("Response headers does not include a reset timestamp");
158166
}
159-
} else {
160-
log::debug!("Response headers does not include a reset timestamp");
167+
return Err(anyhow!("REST API rate limit exceeded!"));
161168
}
162-
return Err(anyhow!("REST API rate limit exceeded!"));
163-
}
164169

165-
// check if secondary rate limit is violated; backoff and try again.
166-
if retries > 4 {
167-
return Err(anyhow!("REST API secondary rate limit exceeded"));
168-
}
169-
if let Some(retry_value) = response.headers().get(&rate_limit_headers.retry) {
170-
if let Ok(retry_str) = retry_value.to_str() {
171-
if let Ok(retry) = retry_str.parse::<u64>() {
172-
let interval = Duration::from_secs(retry + retries.pow(2));
173-
tokio::time::sleep(interval).await;
174-
} else {
175-
log::debug!(
170+
// check if secondary rate limit is violated; backoff and try again.
171+
if i >= 4 {
172+
break;
173+
}
174+
if let Some(retry_value) = response.headers().get(&rate_limit_headers.retry)
175+
{
176+
if let Ok(retry_str) = retry_value.to_str() {
177+
if let Ok(retry) = retry_str.parse::<u64>() {
178+
let interval = Duration::from_secs(retry + i.pow(2));
179+
tokio::time::sleep(interval).await;
180+
} else {
181+
log::debug!(
176182
"Failed to parse u64 from retry interval about rate limit: {retry_str}"
177183
);
184+
}
178185
}
186+
continue;
179187
}
180-
return Self::send_api_request(
181-
client,
182-
request,
183-
rate_limit_headers,
184-
retries + 1,
185-
)
186-
.await;
187188
}
189+
return result.map_err(Error::from);
188190
}
191+
return result.map_err(Error::from);
189192
}
190-
result.map_err(Error::from)
193+
Err(anyhow!("REST API secondary rate limit exceeded"))
191194
}
192-
.boxed()
193195
}
194196

195197
/// A way to get the list of changed files using REST API calls. It is this method's

0 commit comments

Comments
 (0)