torn_api/
local.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    #[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}