use crate::error::Error::{self, *};
use crate::error::{Result, TwitterErrors};
use hyper::client::ResponseFuture;
use hyper::{self, Body, Request};
#[cfg(feature = "hyper-rustls")]
use hyper_rustls::HttpsConnector;
#[cfg(feature = "native_tls")]
use hyper_tls::HttpsConnector;
use serde::{de::DeserializeOwned, Deserialize};
use serde_json;
use std::convert::TryFrom;
use super::Headers;
const X_RATE_LIMIT_LIMIT: &'static str = "X-Rate-Limit-Limit";
const X_RATE_LIMIT_REMAINING: &'static str = "X-Rate-Limit-Remaining";
const X_RATE_LIMIT_RESET: &'static str = "X-Rate-Limit-Reset";
fn rate_limit(headers: &Headers, header: &'static str) -> Result<Option<i32>> {
let val = headers.get(header);
if let Some(val) = val {
let val = val.to_str()?.parse::<i32>()?;
Ok(Some(val))
} else {
Ok(None)
}
}
fn rate_limit_limit(headers: &Headers) -> Result<Option<i32>> {
rate_limit(headers, X_RATE_LIMIT_LIMIT)
}
fn rate_limit_remaining(headers: &Headers) -> Result<Option<i32>> {
rate_limit(headers, X_RATE_LIMIT_REMAINING)
}
fn rate_limit_reset(headers: &Headers) -> Result<Option<i32>> {
rate_limit(headers, X_RATE_LIMIT_RESET)
}
#[derive(
Debug, Deserialize, derive_more::Constructor, derive_more::Deref, derive_more::DerefMut,
)]
pub struct Response<T> {
#[serde(flatten)]
pub rate_limit_status: RateLimit,
#[deref]
#[deref_mut]
#[serde(default)]
pub response: T,
}
impl<T> Response<T> {
pub fn map<F, U>(src: Response<T>, fun: F) -> Response<U>
where
F: FnOnce(T) -> U,
{
Response {
rate_limit_status: src.rate_limit_status,
response: fun(src.response),
}
}
}
pub fn get_response(request: Request<Body>) -> ResponseFuture {
let connector = HttpsConnector::new();
let client = hyper::Client::builder().build(connector);
client.request(request)
}
pub async fn raw_request(request: Request<Body>) -> Result<(Headers, Vec<u8>)> {
let connector = HttpsConnector::new();
let client = hyper::Client::builder().build(connector);
let resp = client.request(request).await?;
let (parts, body) = resp.into_parts();
let body: Vec<_> = hyper::body::to_bytes(body).await?.to_vec();
if let Ok(errors) = serde_json::from_slice::<TwitterErrors>(&body) {
if errors.errors.iter().any(|e| e.code == 88)
&& parts.headers.contains_key(X_RATE_LIMIT_RESET)
{
return Err(RateLimit(rate_limit_reset(&parts.headers)?.unwrap()));
} else {
return Err(TwitterError(parts.headers, errors));
}
}
if !parts.status.is_success() {
return Err(BadStatus(parts.status));
}
Ok((parts.headers, body))
}
pub async fn request_with_json_response<T: DeserializeOwned>(
request: Request<Body>,
) -> Result<Response<T>> {
let (headers, body) = raw_request(request).await?;
let response = serde_json::from_slice(&body)?;
let rate_limit_status = RateLimit::try_from(&headers)?;
Ok(Response {
rate_limit_status,
response,
})
}
#[derive(Copy, Clone, Debug, Deserialize)]
pub struct RateLimit {
pub limit: i32,
pub remaining: i32,
pub reset: i32,
}
impl TryFrom<&Headers> for RateLimit {
type Error = Error;
fn try_from(headers: &Headers) -> Result<Self> {
Ok(Self {
limit: rate_limit_limit(headers)?.unwrap_or(-1),
remaining: rate_limit_remaining(headers)?.unwrap_or(-1),
reset: rate_limit_reset(headers)?.unwrap_or(-1),
})
}
}