1use std::thread;
2
3use reqwest::{RequestBuilder, Response};
4
5use crate::{
6 ratelimit::limiter::{
7 LimiterOutcome, Policy, RateLimiter, RateLimiterError, Rule, RuleSet, RuleState, RuleType,
8 },
9 Client, ClientError,
10};
11
12impl<L: RateLimiter> Client<L> {
13 pub(crate) async fn fetch_api_response(
14 &mut self,
15 endpoint: &str,
16 request: RequestBuilder,
17 ) -> Result<Response, ClientError> {
18 tracing::debug!("recieved request for endpoint: {endpoint}");
19
20 let limiter_outcome = match self.limiter.check(endpoint).await {
21 Ok(o) => o,
22 Err(e) => match e {
23 RateLimiterError::UnknownEndpoint(_) => LimiterOutcome::Proceed,
24 _ => return Err(ClientError::UnknownError),
25 },
26 };
27
28 match limiter_outcome {
29 LimiterOutcome::Proceed => {
30 tracing::debug!("rate limiter decided to proceed");
31 }
32 LimiterOutcome::Retry { after } => {
33 tracing::debug!(
34 "rate limiter decided to wait for {} seconds, sleeping!",
35 after.as_secs()
36 );
37 thread::sleep(after);
38 }
39 };
40
41 let response = request.send().await.map_err(ClientError::SendFailed)?;
42
43 let headers = response.headers();
44 if headers.get("x-rate-limit-policy").is_some() {
45 tracing::debug!(
46 "rate limit headers detected, updating rate limit policy for endpoint: {endpoint}"
47 );
48 let rules_raw = headers
49 .get("x-rate-limit-rules")
50 .unwrap()
51 .to_str()
52 .unwrap()
53 .split(',')
54 .map(RuleType::from)
55 .collect::<Vec<RuleType>>();
56
57 let mut rules: Vec<Rule> = Vec::new();
58 for rr in rules_raw {
59 if let (Some(rset), Some(rstate)) = (
60 headers.get(format!("x-rate-limit-{rr}")),
61 headers.get(format!("x-rate-limit-{rr}-state")),
62 ) {
63 let ruleset = RuleSet::try_from(rset.to_str().unwrap())
64 .map_err(ClientError::RateLimiterRuleError)?;
65
66 let rulestate = RuleState::try_from(rstate.to_str().unwrap())
67 .map_err(ClientError::RateLimiterRuleError)?;
68
69 let rule = Rule {
70 rtype: rr,
71 ruleset,
72 state: rulestate,
73 };
74
75 rules.push(rule);
76 }
77 }
78
79 let policy = Policy { rules };
80
81 self.limiter
82 .update(endpoint, policy)
83 .await
84 .map_err(ClientError::RateLimiterRuleError)?;
85 };
86
87 Ok(response)
88 }
89}