use core::panic;
use std::future::Future;
use memo_map::MemoMap;
use reqwest::{Client, Method, RequestBuilder};
#[cfg(feature = "tracing")]
use tracing as log;
use crate::req::RegionalRequester;
use crate::{ResponseInfo, Result, RiotApiConfig, RiotApiError, TryRequestError, TryRequestResult};
pub struct RiotApi {
config: RiotApiConfig,
client: Client,
regional_requesters: MemoMap<&'static str, RegionalRequester>,
}
impl RiotApi {
pub fn new(config: impl Into<RiotApiConfig>) -> Self {
let mut config = config.into();
let client_builder = config
.client_builder
.take()
.expect("CLIENT_BUILDER IN CONFIG SHOULD NOT BE NONE.");
Self {
config,
client: client_builder
.build()
.expect("Failed to create client from builder."),
regional_requesters: MemoMap::new(),
}
}
pub fn request(&self, method: Method, region_platform: &str, path: &str) -> RequestBuilder {
let base_url_platform = self.config.base_url.replace("{}", region_platform);
self.client
.request(method, format!("{}{}", base_url_platform, path))
}
pub async fn execute_val<'a, T: for<'de> crate::de::Deserialize<'de> + 'a>(
&'a self,
method_id: &'static str,
region_platform: &'static str,
request: RequestBuilder,
) -> Result<T> {
let rinfo = self
.execute_raw(method_id, region_platform, request)
.await?;
rinfo.json::<T>().await
}
pub async fn execute_opt<'a, T: for<'de> crate::de::Deserialize<'de> + 'a>(
&'a self,
method_id: &'static str,
region_platform: &'static str,
request: RequestBuilder,
) -> Result<Option<T>> {
let rinfo = self
.execute_raw(method_id, region_platform, request)
.await?;
if rinfo.status_none {
return Ok(None);
}
rinfo.json::<Option<T>>().await
}
pub async fn execute(
&self,
method_id: &'static str,
region_platform: &'static str,
request: RequestBuilder,
) -> Result<()> {
let rinfo = self
.execute_raw(method_id, region_platform, request)
.await?;
let retries = rinfo.retries;
let status = rinfo.response.status();
if status.is_client_error() || status.is_server_error() {
Err(RiotApiError::new(
rinfo.reqwest_errors,
None,
retries,
Some(rinfo.response),
Some(status),
))
} else {
Ok(())
}
}
pub async fn try_execute_val<'a, T: for<'de> crate::de::Deserialize<'de> + 'a>(
&'a self,
method_id: &'static str,
region_platform: &'static str,
request: RequestBuilder,
min_capacity: f32,
) -> TryRequestResult<T> {
let rinfo = self
.try_execute_raw(method_id, region_platform, request, min_capacity)
.await?;
Ok(rinfo.json::<T>().await?)
}
pub async fn try_execute_opt<'a, T: for<'de> crate::de::Deserialize<'de> + 'a>(
&'a self,
method_id: &'static str,
region_platform: &'static str,
request: RequestBuilder,
min_capacity: f32,
) -> TryRequestResult<Option<T>> {
let rinfo = self
.try_execute_raw(method_id, region_platform, request, min_capacity)
.await?;
if rinfo.status_none {
return Ok(None);
}
Ok(rinfo.json::<Option<T>>().await?)
}
pub async fn try_execute(
&self,
method_id: &'static str,
region_platform: &'static str,
request: RequestBuilder,
min_capacity: f32,
) -> TryRequestResult<()> {
let rinfo = self
.try_execute_raw(method_id, region_platform, request, min_capacity)
.await?;
let retries = rinfo.retries;
let status = rinfo.response.status();
if status.is_client_error() || status.is_server_error() {
Err(TryRequestError::RiotApiError(RiotApiError::new(
rinfo.reqwest_errors,
None,
retries,
Some(rinfo.response),
Some(status),
)))
} else {
Ok(())
}
}
pub async fn execute_raw(
&self,
method_id: &'static str,
region_platform: &'static str,
request: RequestBuilder,
) -> Result<ResponseInfo> {
self.regional_requester(region_platform)
.execute(&self.config, method_id, request, None)
.await
.map_err(|e| match e {
TryRequestError::NotEnoughCapacity => {
panic!("execute called with no capacity requirement, should not happen.")
}
TryRequestError::RiotApiError(e) => e,
})
}
pub fn try_execute_raw(
&self,
method_id: &'static str,
region_platform: &'static str,
request: RequestBuilder,
min_capacity: f32,
) -> impl Future<Output = TryRequestResult<ResponseInfo>> + '_ {
self.regional_requester(region_platform).execute(
&self.config,
method_id,
request,
Some(min_capacity),
)
}
pub(crate) fn get_rso_clear_header(&self) -> Option<&str> {
self.config.rso_clear_header.as_deref()
}
fn regional_requester(&self, region_platform: &'static str) -> &RegionalRequester {
self.regional_requesters
.get_or_insert(®ion_platform, || {
log::debug!(
"Creating requester for region platform {}.",
region_platform
);
RegionalRequester::new()
})
}
}