use http::{header, StatusCode};
use serde::de::DeserializeOwned;
use crate::{
allowance::{AllowanceCheck, AllowanceCheckURL},
area_info::{AreaInfo, AreaInfoURLBuilder},
area_nearby::{AreaNearby, AreasNearbyURLBuilder},
area_search::{AreaSearch, AreaSearchURLBuilder},
constants::TOKEN_KEY,
errors::{APIError, HttpError},
get_token_from_env,
status::{EskomStatus, EskomStatusUrl},
topics_nearby::{TopicsNearby, TopicsNearbyUrlBuilder},
EndpointAsync,
};
pub struct ReqwestAsyncCLient {
client: reqwest::Client,
}
impl ReqwestAsyncCLient {
pub fn new(token: String) -> Self {
let mut headers = header::HeaderMap::new();
headers.insert(
TOKEN_KEY,
header::HeaderValue::from_str(token.as_str()).unwrap(),
);
ReqwestAsyncCLient {
client: reqwest::ClientBuilder::new()
.default_headers(headers)
.build()
.unwrap(),
}
}
pub fn new_with_env(var_name: Option<&str>) -> Self {
match get_token_from_env(var_name) {
Ok(val) => {
let mut headers = header::HeaderMap::new();
headers.insert(
TOKEN_KEY,
header::HeaderValue::from_str(val.as_str()).unwrap(),
);
ReqwestAsyncCLient {
client: reqwest::ClientBuilder::new()
.default_headers(headers)
.build()
.unwrap(),
}
}
Err(e) => panic!("Error: {}", e),
}
}
pub async fn get_load_shedding_status(&self) -> Result<EskomStatus, HttpError> {
let c = EskomStatusUrl::default();
c.reqwest_client_async(&self.client).await
}
pub async fn get_area_info(&self, area_id: &str) -> Result<AreaInfo, HttpError> {
let t = AreaInfoURLBuilder::default()
.area_id(area_id.to_owned())
.build()
.map_err(|_| HttpError::AreaIdNotSet)?;
t.reqwest_client_async(&self.client).await
}
pub async fn areas_nearby(&self, lat: f32, long: f32) -> Result<AreaNearby, HttpError> {
let t = AreasNearbyURLBuilder::default()
.latitude(lat)
.longitude(long)
.build()
.map_err(|_| HttpError::LongitudeOrLatitudeNotSet {
longitude: lat,
latitude: long,
})?;
t.reqwest_client_async(&self.client).await
}
pub async fn areas_search(&self, search_term: &str) -> Result<AreaSearch, HttpError> {
let t = AreaSearchURLBuilder::default()
.search_term(search_term)
.build()
.map_err(|_| HttpError::SearchTextNotSet)?;
t.reqwest_client_async(&self.client).await
}
pub async fn topics_nearby(&self, lat: f32, long: f32) -> Result<TopicsNearby, HttpError> {
let t = TopicsNearbyUrlBuilder::default()
.latitude(lat)
.longitude(long)
.build()
.map_err(|_| HttpError::LongitudeOrLatitudeNotSet {
longitude: lat,
latitude: long,
})?;
t.reqwest_client_async(&self.client).await
}
pub async fn check_allowance(&self) -> Result<AllowanceCheck, HttpError> {
let t = AllowanceCheckURL::default();
t.reqwest_client_async(&self.client).await
}
}
pub async fn handle_reqwest_response<T: DeserializeOwned>(
response: Result<reqwest::Response, reqwest::Error>,
) -> Result<T, HttpError> {
match response {
Ok(resp) => {
let status_code = resp.status();
if status_code.is_server_error() {
Err(HttpError::ResponseError(
resp.error_for_status().unwrap_err(),
))
} else {
match status_code {
StatusCode::BAD_REQUEST => Err(HttpError::APIError(APIError::BadRequest)),
StatusCode::FORBIDDEN => Err(HttpError::APIError(APIError::Forbidden)),
StatusCode::NOT_FOUND => Err(HttpError::APIError(APIError::NotFound)),
StatusCode::TOO_MANY_REQUESTS => Err(HttpError::APIError(APIError::TooManyRequests)),
_ => {
let r = resp.json::<T>().await;
match r {
Ok(r) => Ok(r),
Err(e) => {
if e.is_decode() {
Err(HttpError::ResponseError(e))
} else {
Err(HttpError::Unknown)
}
}
}
}
}
}
}
Err(err) => {
if err.is_timeout() {
Err(HttpError::Timeout)
} else if err.is_status() {
Err(HttpError::ResponseError(err))
} else {
Err(HttpError::NoInternet)
}
}
}
}