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, { client: &'a C, executor: E, } impl<'a, C, E> ApiProvider<'a, C, E> where C: ApiClient, E: RequestExecutor, { pub fn new(client: &'a C, executor: E) -> ApiProvider<'a, C, E> { Self { client, executor } } #[cfg(feature = "user")] pub async fn user(&self, build: F) -> Result where F: FnOnce( crate::ApiRequestBuilder, ) -> crate::ApiRequestBuilder, { 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( &self, ids: L, build: F, ) -> HashMap> where F: FnOnce( crate::ApiRequestBuilder, ) -> crate::ApiRequestBuilder, I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync, L: IntoIterator, { 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(&self, build: F) -> Result where F: FnOnce( crate::ApiRequestBuilder, ) -> crate::ApiRequestBuilder, { 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( &self, ids: L, build: F, ) -> HashMap> where F: FnOnce( crate::ApiRequestBuilder, ) -> crate::ApiRequestBuilder, I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync, L: IntoIterator, { 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 = "market")] pub async fn market(&self, build: F) -> Result where F: FnOnce( crate::ApiRequestBuilder, ) -> crate::ApiRequestBuilder, { let mut builder = crate::ApiRequestBuilder::default(); builder = build(builder); self.executor .execute(self.client, builder.request, builder.id) .await .map(crate::market::Response::from_response) } #[cfg(feature = "market")] pub async fn markets( &self, ids: L, build: F, ) -> HashMap> where F: FnOnce( crate::ApiRequestBuilder, ) -> crate::ApiRequestBuilder, I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync, L: IntoIterator, { 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::market::Response::from_response))) .collect() } #[cfg(feature = "torn")] pub async fn torn(&self, build: F) -> Result where F: FnOnce( crate::ApiRequestBuilder, ) -> crate::ApiRequestBuilder, { 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( &self, ids: L, build: F, ) -> HashMap> where F: FnOnce( crate::ApiRequestBuilder, ) -> crate::ApiRequestBuilder, I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync, L: IntoIterator, { 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(&self, build: F) -> Result where F: FnOnce( crate::ApiRequestBuilder, ) -> crate::ApiRequestBuilder, { 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 where C: ApiClient, { type Error: std::error::Error + Send + Sync; async fn execute( &self, client: &C, request: ApiRequest, id: Option, ) -> Result where A: ApiSelection; async fn execute_many( &self, client: &C, request: ApiRequest, ids: Vec, ) -> HashMap> where A: ApiSelection, I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync; } #[async_trait] impl RequestExecutor for DirectExecutor where C: ApiClient, { type Error = ApiClientError; async fn execute( &self, client: &C, request: ApiRequest, id: Option, ) -> Result 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( &self, client: &C, request: ApiRequest, ids: Vec, ) -> HashMap> 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; fn torn_api(&self, key: S) -> ApiProvider> where Self: Sized, S: ToString, { ApiProvider::new(self, DirectExecutor::new(key.to_string())) } }