torn-api.rs/torn-api/src/send.rs

258 lines
7.2 KiB
Rust

use std::collections::HashMap;
use async_trait::async_trait;
use crate::{
ApiCategoryResponse, ApiClientError, ApiRequest, ApiResponse, ApiSelection, DirectExecutor,
};
pub struct ApiProvider<'a, C, E>
where
C: ApiClient,
E: RequestExecutor<C>,
{
#[allow(dead_code)]
client: &'a C,
#[allow(dead_code)]
executor: E,
}
impl<'a, C, E> ApiProvider<'a, C, E>
where
C: ApiClient,
E: RequestExecutor<C>,
{
pub fn new(client: &'a C, executor: E) -> ApiProvider<'a, C, E> {
Self { client, executor }
}
#[cfg(feature = "user")]
pub async fn user<F>(&self, build: F) -> Result<crate::user::Response, E::Error>
where
F: FnOnce(
crate::ApiRequestBuilder<crate::user::Selection>,
) -> crate::ApiRequestBuilder<crate::user::Selection>,
{
let mut builder = crate::ApiRequestBuilder::default();
builder = build(builder);
self.executor
.execute(self.client, builder.request, builder.id)
.await
.map(crate::user::Response::from_response)
}
#[cfg(feature = "user")]
pub async fn users<F, L, I>(
&self,
ids: L,
build: F,
) -> HashMap<I, Result<crate::user::Response, E::Error>>
where
F: FnOnce(
crate::ApiRequestBuilder<crate::user::Selection>,
) -> crate::ApiRequestBuilder<crate::user::Selection>,
I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync,
L: IntoIterator<Item = I>,
{
let mut builder = crate::ApiRequestBuilder::default();
builder = build(builder);
self.executor
.execute_many(self.client, builder.request, Vec::from_iter(ids))
.await
.into_iter()
.map(|(k, v)| (k, v.map(crate::user::Response::from_response)))
.collect()
}
#[cfg(feature = "faction")]
pub async fn faction<F>(&self, build: F) -> Result<crate::faction::Response, E::Error>
where
F: FnOnce(
crate::ApiRequestBuilder<crate::faction::Selection>,
) -> crate::ApiRequestBuilder<crate::faction::Selection>,
{
let mut builder = crate::ApiRequestBuilder::default();
builder = build(builder);
self.executor
.execute(self.client, builder.request, builder.id)
.await
.map(crate::faction::Response::from_response)
}
#[cfg(feature = "faction")]
pub async fn factions<F, L, I>(
&self,
ids: L,
build: F,
) -> HashMap<I, Result<crate::faction::Response, E::Error>>
where
F: FnOnce(
crate::ApiRequestBuilder<crate::faction::Selection>,
) -> crate::ApiRequestBuilder<crate::faction::Selection>,
I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync,
L: IntoIterator<Item = I>,
{
let mut builder = crate::ApiRequestBuilder::default();
builder = build(builder);
self.executor
.execute_many(self.client, builder.request, Vec::from_iter(ids))
.await
.into_iter()
.map(|(k, v)| (k, v.map(crate::faction::Response::from_response)))
.collect()
}
#[cfg(feature = "torn")]
pub async fn torn<F>(&self, build: F) -> Result<crate::torn::Response, E::Error>
where
F: FnOnce(
crate::ApiRequestBuilder<crate::torn::Selection>,
) -> crate::ApiRequestBuilder<crate::torn::Selection>,
{
let mut builder = crate::ApiRequestBuilder::default();
builder = build(builder);
self.executor
.execute(self.client, builder.request, builder.id)
.await
.map(crate::torn::Response::from_response)
}
#[cfg(feature = "torn")]
pub async fn torns<F, L, I>(
&self,
ids: L,
build: F,
) -> HashMap<I, Result<crate::torn::Response, E::Error>>
where
F: FnOnce(
crate::ApiRequestBuilder<crate::torn::Selection>,
) -> crate::ApiRequestBuilder<crate::torn::Selection>,
I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync,
L: IntoIterator<Item = I>,
{
let mut builder = crate::ApiRequestBuilder::default();
builder = build(builder);
self.executor
.execute_many(self.client, builder.request, Vec::from_iter(ids))
.await
.into_iter()
.map(|(k, v)| (k, v.map(crate::torn::Response::from_response)))
.collect()
}
#[cfg(feature = "key")]
pub async fn key<F>(&self, build: F) -> Result<crate::key::Response, E::Error>
where
F: FnOnce(
crate::ApiRequestBuilder<crate::key::Selection>,
) -> crate::ApiRequestBuilder<crate::key::Selection>,
{
let mut builder = crate::ApiRequestBuilder::default();
builder = build(builder);
self.executor
.execute(self.client, builder.request, builder.id)
.await
.map(crate::key::Response::from_response)
}
}
#[async_trait]
pub trait RequestExecutor<C>
where
C: ApiClient,
{
type Error: std::error::Error + Send + Sync;
async fn execute<A>(
&self,
client: &C,
request: ApiRequest<A>,
id: Option<String>,
) -> Result<ApiResponse, Self::Error>
where
A: ApiSelection;
async fn execute_many<A, I>(
&self,
client: &C,
request: ApiRequest<A>,
ids: Vec<I>,
) -> HashMap<I, Result<ApiResponse, Self::Error>>
where
A: ApiSelection,
I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync;
}
#[async_trait]
impl<C> RequestExecutor<C> for DirectExecutor<C>
where
C: ApiClient,
{
type Error = ApiClientError<C::Error>;
async fn execute<A>(
&self,
client: &C,
request: ApiRequest<A>,
id: Option<String>,
) -> Result<ApiResponse, Self::Error>
where
A: ApiSelection,
{
let url = request.url(&self.key, id.as_deref());
let value = client.request(url).await.map_err(ApiClientError::Client)?;
Ok(ApiResponse::from_value(value)?)
}
async fn execute_many<A, I>(
&self,
client: &C,
request: ApiRequest<A>,
ids: Vec<I>,
) -> HashMap<I, Result<ApiResponse, Self::Error>>
where
A: ApiSelection,
I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync,
{
let request_ref = &request;
let tuples = futures::future::join_all(ids.into_iter().map(|i| async move {
let id_string = i.to_string();
let url = request_ref.url(&self.key, Some(&id_string));
let value = client.request(url).await.map_err(ApiClientError::Client);
(
i,
value.and_then(|v| ApiResponse::from_value(v).map_err(Into::into)),
)
}))
.await;
HashMap::from_iter(tuples)
}
}
#[async_trait]
pub trait ApiClient: Send + Sync {
type Error: std::error::Error + Sync + Send;
async fn request(&self, url: String) -> Result<serde_json::Value, Self::Error>;
fn torn_api<S>(&self, key: S) -> ApiProvider<Self, DirectExecutor<Self>>
where
Self: Sized,
S: ToString,
{
ApiProvider::new(self, DirectExecutor::new(key.to_string()))
}
}