1use crate::error::APIError;
15use crate::error::Result;
16use crate::throttler::{RateLimitStore, RequestType};
17use crate::APIWrapper;
18
19use reqwest::{Response, StatusCode};
20use serde::{de::DeserializeOwned, Deserialize, Serialize};
21use tokio::time::Duration;
22
23#[derive(Deserialize)]
25pub struct APIResponse<D> {
26 pub result: String,
27 pub data: Option<D>,
28 pub error: Option<APIError>,
29}
30
31impl<D> APIResponse<D> {
32 pub fn is_success(&self) -> bool {
34 self.result == "success"
35 }
36
37 pub fn data(self) -> D {
41 self.data.expect("no data present")
42 }
43
44 pub fn error(self) -> APIError {
48 self.error.expect("no error present")
49 }
50
51 pub fn as_result(self) -> Result<D> {
52 if self.is_success() {
53 Ok(self.data())
54 } else {
55 Err(self.error())
56 }
57 }
58}
59
60pub async fn get<D>(wrapper: &APIWrapper, endpoint: &str) -> Result<APIResponse<D>>
61where
62 D: DeserializeOwned,
63{
64 loop {
65 loop {
66 match crate::throttler::stall_for(&wrapper.rate_limit_store, RequestType::READ) {
67 0 => break,
68 stall_for => tokio::time::sleep(Duration::from_millis(stall_for)).await,
69 };
70 }
71
72 let response = wrapper.http_client.get(endpoint).send().await?;
73
74 if !did_hit_limit(&wrapper.rate_limit_store, &response, RequestType::READ) {
75 return Ok(response.json().await?);
76 }
77 }
78}
79
80pub async fn post<D, B>(wrapper: &APIWrapper, endpoint: &str, body: &B) -> Result<APIResponse<D>>
81where
82 D: DeserializeOwned,
83 B: Serialize,
84{
85 loop {
86 loop {
87 match crate::throttler::stall_for(&wrapper.rate_limit_store, RequestType::WRITE) {
88 0 => break,
89 stall_for => tokio::time::sleep(Duration::from_millis(stall_for)).await,
90 };
91 }
92
93 let response = wrapper.http_client.post(endpoint).json(body).send().await?;
94
95 if !did_hit_limit(&wrapper.rate_limit_store, &response, RequestType::WRITE) {
96 return Ok(response.json().await?);
97 }
98 }
99}
100
101pub async fn patch<D, B>(wrapper: &APIWrapper, endpoint: &str, body: &B) -> Result<APIResponse<D>>
102where
103 D: DeserializeOwned,
104 B: Serialize,
105{
106 loop {
107 loop {
108 match crate::throttler::stall_for(&wrapper.rate_limit_store, RequestType::WRITE) {
109 0 => break,
110 stall_for => tokio::time::sleep(Duration::from_millis(stall_for)).await,
111 };
112 }
113
114 let response = wrapper.http_client.post(endpoint).json(body).send().await?;
115
116 if !did_hit_limit(&wrapper.rate_limit_store, &response, RequestType::WRITE) {
117 return Ok(response.json().await?);
118 }
119 }
120}
121
122pub async fn delete<D>(wrapper: &APIWrapper, endpoint: &str) -> Result<APIResponse<D>>
123where
124 D: DeserializeOwned,
125{
126 loop {
127 loop {
128 match crate::throttler::stall_for(&wrapper.rate_limit_store, RequestType::WRITE) {
129 0 => break,
130 stall_for => tokio::time::sleep(Duration::from_millis(stall_for)).await,
131 };
132 }
133
134 let response = wrapper.http_client.delete(endpoint).send().await?;
135
136 if !did_hit_limit(&wrapper.rate_limit_store, &response, RequestType::WRITE) {
137 return response.json().await?;
138 }
139 }
140}
141
142fn did_hit_limit(store: &RateLimitStore, response: &Response, request_type: RequestType) -> bool {
143 if response.status() != StatusCode::TOO_MANY_REQUESTS {
144 match &request_type {
145 RequestType::READ => store.reset_read(),
146 RequestType::WRITE => store.reset_write(),
147 };
148
149 return false;
150 }
151
152 let retry = response.headers().get("Retry-After").expect("no retry-after header present");
153 let retry: u64 = retry.to_str().expect("non-ascii characters present").parse().expect("not a valid u64 int");
154
155 match &request_type {
156 RequestType::READ => store.store_read(retry),
157 RequestType::WRITE => store.store_write(retry),
158 };
159
160 true
161}