torn_api/
send.rs

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}