1use std::collections::HashMap;
2
3use async_trait::async_trait;
4
5use crate::{ApiClientError, ApiRequest, ApiResponse, ApiSelection, DirectExecutor};
6
7pub struct ApiProvider<'a, C, E>
8where
9 C: ApiClient,
10 E: RequestExecutor<C>,
11{
12 client: &'a C,
13 executor: E,
14}
15
16impl<'a, C, E> ApiProvider<'a, C, E>
17where
18 C: ApiClient,
19 E: RequestExecutor<C>,
20{
21 pub fn new(client: &'a C, executor: E) -> ApiProvider<'a, C, E> {
22 Self { client, executor }
23 }
24
25 #[cfg(feature = "user")]
26 pub async fn user<F>(&self, build: F) -> Result<crate::user::Response, E::Error>
27 where
28 F: FnOnce(
29 crate::ApiRequestBuilder<crate::user::Selection>,
30 ) -> crate::ApiRequestBuilder<crate::user::Selection>,
31 {
32 let mut builder = crate::ApiRequestBuilder::default();
33 builder = build(builder);
34
35 self.executor
36 .execute(self.client, builder.request, builder.id)
37 .await
38 }
39
40 #[cfg(feature = "user")]
41 pub async fn users<F, L, I>(
42 &self,
43 ids: L,
44 build: F,
45 ) -> HashMap<I, Result<crate::user::Response, E::Error>>
46 where
47 F: FnOnce(
48 crate::ApiRequestBuilder<crate::user::Selection>,
49 ) -> crate::ApiRequestBuilder<crate::user::Selection>,
50 I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync,
51 L: IntoIterator<Item = I>,
52 {
53 let mut builder = crate::ApiRequestBuilder::default();
54 builder = build(builder);
55
56 self.executor
57 .execute_many(self.client, builder.request, Vec::from_iter(ids))
58 .await
59 }
60
61 #[cfg(feature = "faction")]
62 pub async fn faction<F>(&self, build: F) -> Result<crate::faction::Response, E::Error>
63 where
64 F: FnOnce(
65 crate::ApiRequestBuilder<crate::faction::Selection>,
66 ) -> crate::ApiRequestBuilder<crate::faction::Selection>,
67 {
68 let mut builder = crate::ApiRequestBuilder::default();
69 builder = build(builder);
70
71 self.executor
72 .execute(self.client, builder.request, builder.id)
73 .await
74 }
75
76 #[cfg(feature = "faction")]
77 pub async fn factions<F, L, I>(
78 &self,
79 ids: L,
80 build: F,
81 ) -> HashMap<I, Result<crate::faction::Response, E::Error>>
82 where
83 F: FnOnce(
84 crate::ApiRequestBuilder<crate::faction::Selection>,
85 ) -> crate::ApiRequestBuilder<crate::faction::Selection>,
86 I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync,
87 L: IntoIterator<Item = I>,
88 {
89 let mut builder = crate::ApiRequestBuilder::default();
90 builder = build(builder);
91
92 self.executor
93 .execute_many(self.client, builder.request, Vec::from_iter(ids))
94 .await
95 }
96
97 #[cfg(feature = "market")]
98 pub async fn market<F>(&self, build: F) -> Result<crate::market::Response, E::Error>
99 where
100 F: FnOnce(
101 crate::ApiRequestBuilder<crate::market::MarketSelection>,
102 ) -> crate::ApiRequestBuilder<crate::market::MarketSelection>,
103 {
104 let mut builder = crate::ApiRequestBuilder::default();
105 builder = build(builder);
106
107 self.executor
108 .execute(self.client, builder.request, builder.id)
109 .await
110 }
111
112 #[cfg(feature = "market")]
113 pub async fn markets<F, L, I>(
114 &self,
115 ids: L,
116 build: F,
117 ) -> HashMap<I, Result<crate::market::Response, E::Error>>
118 where
119 F: FnOnce(
120 crate::ApiRequestBuilder<crate::market::MarketSelection>,
121 ) -> crate::ApiRequestBuilder<crate::market::MarketSelection>,
122 I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync,
123 L: IntoIterator<Item = I>,
124 {
125 let mut builder = crate::ApiRequestBuilder::default();
126 builder = build(builder);
127
128 self.executor
129 .execute_many(self.client, builder.request, Vec::from_iter(ids))
130 .await
131 }
132
133 #[cfg(feature = "torn")]
134 pub async fn torn<F>(&self, build: F) -> Result<crate::torn::Response, E::Error>
135 where
136 F: FnOnce(
137 crate::ApiRequestBuilder<crate::torn::Selection>,
138 ) -> crate::ApiRequestBuilder<crate::torn::Selection>,
139 {
140 let mut builder = crate::ApiRequestBuilder::default();
141 builder = build(builder);
142
143 self.executor
144 .execute(self.client, builder.request, builder.id)
145 .await
146 }
147
148 #[cfg(feature = "torn")]
149 pub async fn torns<F, L, I>(
150 &self,
151 ids: L,
152 build: F,
153 ) -> HashMap<I, Result<crate::torn::Response, E::Error>>
154 where
155 F: FnOnce(
156 crate::ApiRequestBuilder<crate::torn::Selection>,
157 ) -> crate::ApiRequestBuilder<crate::torn::Selection>,
158 I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync,
159 L: IntoIterator<Item = I>,
160 {
161 let mut builder = crate::ApiRequestBuilder::default();
162 builder = build(builder);
163
164 self.executor
165 .execute_many(self.client, builder.request, Vec::from_iter(ids))
166 .await
167 }
168
169 #[cfg(feature = "key")]
170 pub async fn key<F>(&self, build: F) -> Result<crate::key::Response, E::Error>
171 where
172 F: FnOnce(
173 crate::ApiRequestBuilder<crate::key::Selection>,
174 ) -> crate::ApiRequestBuilder<crate::key::Selection>,
175 {
176 let mut builder = crate::ApiRequestBuilder::default();
177 builder = build(builder);
178
179 self.executor
180 .execute(self.client, builder.request, builder.id)
181 .await
182 }
183}
184
185#[async_trait]
186pub trait RequestExecutor<C>
187where
188 C: ApiClient,
189{
190 type Error: std::error::Error + Send + Sync;
191
192 async fn execute<A>(
193 &self,
194 client: &C,
195 request: ApiRequest<A>,
196 id: Option<String>,
197 ) -> Result<A::Response, Self::Error>
198 where
199 A: ApiSelection;
200
201 async fn execute_many<A, I>(
202 &self,
203 client: &C,
204 request: ApiRequest<A>,
205 ids: Vec<I>,
206 ) -> HashMap<I, Result<A::Response, Self::Error>>
207 where
208 A: ApiSelection,
209 I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync;
210}
211
212#[async_trait]
213impl<C> RequestExecutor<C> for DirectExecutor<C>
214where
215 C: ApiClient,
216{
217 type Error = ApiClientError<C::Error>;
218
219 async fn execute<A>(
220 &self,
221 client: &C,
222 request: ApiRequest<A>,
223 id: Option<String>,
224 ) -> Result<A::Response, Self::Error>
225 where
226 A: ApiSelection,
227 {
228 let url = request.url(&self.key, id.as_deref());
229
230 let value = client.request(url).await.map_err(ApiClientError::Client)?;
231
232 Ok(ApiResponse::from_value(value)?.into())
233 }
234
235 async fn execute_many<A, I>(
236 &self,
237 client: &C,
238 request: ApiRequest<A>,
239 ids: Vec<I>,
240 ) -> HashMap<I, Result<A::Response, Self::Error>>
241 where
242 A: ApiSelection,
243 I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync,
244 {
245 let request_ref = &request;
246 let tuples = futures::future::join_all(ids.into_iter().map(|i| async move {
247 let id_string = i.to_string();
248 let url = request_ref.url(&self.key, Some(&id_string));
249
250 let value = client.request(url).await.map_err(ApiClientError::Client);
251
252 (
253 i,
254 value.and_then(|v| {
255 ApiResponse::from_value(v)
256 .map(Into::into)
257 .map_err(Into::into)
258 }),
259 )
260 }))
261 .await;
262
263 HashMap::from_iter(tuples)
264 }
265}
266
267#[async_trait]
268pub trait ApiClient: Send + Sync {
269 type Error: std::error::Error + Sync + Send;
270
271 async fn request(&self, url: String) -> Result<serde_json::Value, Self::Error>;
272
273 fn torn_api<S>(&self, key: S) -> ApiProvider<Self, DirectExecutor<Self>>
274 where
275 Self: Sized,
276 S: ToString,
277 {
278 ApiProvider::new(self, DirectExecutor::new(key.to_string()))
279 }
280}