egg_mode/common/
response.rs1use crate::error::Error::{self, *};
5use crate::error::{Result, TwitterErrors};
6
7use hyper::client::{HttpConnector, ResponseFuture};
8use hyper::{self, Body, Request};
9use serde::{de::DeserializeOwned, Deserialize};
10
11use std::convert::TryFrom;
12
13use super::Headers;
14
15const X_RATE_LIMIT_LIMIT: &str = "X-Rate-Limit-Limit";
16const X_RATE_LIMIT_REMAINING: &str = "X-Rate-Limit-Remaining";
17const X_RATE_LIMIT_RESET: &str = "X-Rate-Limit-Reset";
18
19fn rate_limit(headers: &Headers, header: &'static str) -> Result<Option<i32>> {
20 let val = headers.get(header);
21
22 if let Some(val) = val {
23 let val = val.to_str()?.parse::<i32>()?;
24 Ok(Some(val))
25 } else {
26 Ok(None)
27 }
28}
29
30fn rate_limit_limit(headers: &Headers) -> Result<Option<i32>> {
31 rate_limit(headers, X_RATE_LIMIT_LIMIT)
32}
33
34fn rate_limit_remaining(headers: &Headers) -> Result<Option<i32>> {
35 rate_limit(headers, X_RATE_LIMIT_REMAINING)
36}
37
38fn rate_limit_reset(headers: &Headers) -> Result<Option<i32>> {
39 rate_limit(headers, X_RATE_LIMIT_RESET)
40}
41
42#[derive(
52 Debug, Deserialize, derive_more::Constructor, derive_more::Deref, derive_more::DerefMut,
53)]
54pub struct Response<T> {
55 #[serde(flatten)]
57 pub rate_limit_status: RateLimit,
58 #[deref]
60 #[deref_mut]
61 #[serde(default)]
62 pub response: T,
63}
64
65impl<T> Response<T> {
66 pub fn map<F, U>(src: Response<T>, fun: F) -> Response<U>
72 where
73 F: FnOnce(T) -> U,
74 {
75 Response {
76 rate_limit_status: src.rate_limit_status,
77 response: fun(src.response),
78 }
79 }
80
81 pub fn try_map<F, U, E>(src: Response<T>, fun: F) -> std::result::Result<Response<U>, E>
88 where
89 F: FnOnce(T) -> std::result::Result<U, E>,
90 {
91 Ok(Response {
92 rate_limit_status: src.rate_limit_status,
93 response: fun(src.response)?,
94 })
95 }
96
97 pub fn into<U>(src: Self) -> Response<U>
105 where
106 T: Into<U>,
107 {
108 Response {
109 rate_limit_status: src.rate_limit_status,
110 response: src.response.into(),
111 }
112 }
113}
114
115impl<T: IntoIterator> IntoIterator for Response<T> {
116 type IntoIter = ResponseIter<T::IntoIter>;
117 type Item = Response<T::Item>;
118
119 fn into_iter(self) -> Self::IntoIter {
120 ResponseIter {
121 it: Response::map(self, |it| it.into_iter()),
122 }
123 }
124}
125
126pub struct ResponseIter<T> {
132 it: Response<T>,
133}
134
135impl<T: Iterator> Iterator for ResponseIter<T> {
136 type Item = Response<T::Item>;
137
138 fn next(&mut self) -> Option<Self::Item> {
139 Some(Response {
140 rate_limit_status: self.it.rate_limit_status,
141 response: self.it.response.next()?,
142 })
143 }
144}
145
146#[cfg(not(any(feature = "native_tls", feature = "rustls", feature = "rustls_webpki")))]
147compile_error!(
148 "Crate `egg_mode` must be compiled with exactly one of the three \
149feature flags `native_tls`, `rustls` or `rustls_webpki` enabled, you attempted to \
150compile `egg_mode` with none of them enabled"
151);
152
153#[cfg(any(
154 all(
155 feature = "native_tls",
156 any(feature = "rustls", feature = "rustls_webpki")
157 ),
158 all(
159 feature = "rustls",
160 any(feature = "native_tls", feature = "rustls_webpki")
161 ),
162 all(
163 feature = "rustls_webpki",
164 any(feature = "native_tls", feature = "rustls")
165 ),
166))]
167compile_error!(
168 "features `egg_mode/native_tls`, `egg_mode/rustls` and \
169`egg_mode/rustls_webpki` are mutually exclusive, you attempted to compile `egg_mode` \
170with more than one of these feature flags enabled at the same time"
171);
172
173#[cfg(feature = "native_tls")]
174fn new_https_connector() -> hyper_tls::HttpsConnector<HttpConnector> {
175 hyper_tls::HttpsConnector::new()
176}
177
178#[cfg(feature = "rustls")]
179fn new_https_connector() -> hyper_rustls::HttpsConnector<HttpConnector> {
180 hyper_rustls::HttpsConnector::with_native_roots()
181}
182
183#[cfg(feature = "rustls_webpki")]
184fn new_https_connector() -> hyper_rustls::HttpsConnector<HttpConnector> {
185 hyper_rustls::HttpsConnector::with_webpki_roots()
186}
187
188pub fn get_response(request: Request<Body>) -> ResponseFuture {
191 let connector = new_https_connector();
192 let client = hyper::Client::builder().build(connector);
193 client.request(request)
194}
195
196pub async fn raw_request(request: Request<Body>) -> Result<(Headers, Vec<u8>)> {
200 let connector = new_https_connector();
201 let client = hyper::Client::builder().build(connector);
202 let resp = client.request(request).await?;
203 let (parts, body) = resp.into_parts();
204 let body: Vec<_> = hyper::body::to_bytes(body).await?.to_vec();
205 if let Ok(errors) = serde_json::from_slice::<TwitterErrors>(&body) {
206 if errors.errors.iter().any(|e| e.code == 88)
207 && parts.headers.contains_key(X_RATE_LIMIT_RESET)
208 {
209 return Err(RateLimit(rate_limit_reset(&parts.headers)?.unwrap()));
210 } else {
211 return Err(TwitterError(parts.headers, errors));
212 }
213 }
214 if !parts.status.is_success() {
215 return Err(BadStatus(parts.status));
216 }
217 Ok((parts.headers, body))
218}
219
220pub async fn request_with_empty_response(request: Request<Body>) -> Result<Response<()>> {
224 let (headers, _) = raw_request(request).await?;
225 let rate_limit_status = RateLimit::try_from(&headers)?;
226 Ok(Response {
227 rate_limit_status,
228 response: (),
229 })
230}
231
232pub async fn request_with_json_response<T: DeserializeOwned>(
236 request: Request<Body>,
237) -> Result<Response<T>> {
238 let (headers, body) = raw_request(request).await?;
239 let response = serde_json::from_slice(&body)?;
240 let rate_limit_status = RateLimit::try_from(&headers)?;
241 Ok(Response {
242 rate_limit_status,
243 response,
244 })
245}
246
247#[derive(Copy, Clone, Debug, Deserialize)]
266pub struct RateLimit {
267 pub limit: i32,
269 pub remaining: i32,
271 pub reset: i32,
273}
274
275impl TryFrom<&Headers> for RateLimit {
276 type Error = Error;
277 fn try_from(headers: &Headers) -> Result<Self> {
278 Ok(Self {
279 limit: rate_limit_limit(headers)?.unwrap_or(-1),
280 remaining: rate_limit_remaining(headers)?.unwrap_or(-1),
281 reset: rate_limit_reset(headers)?.unwrap_or(-1),
282 })
283 }
284}