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