1pub mod types;
4
5use http_body_util::BodyExt;
6use hyper::Uri;
7use serde::de::DeserializeOwned;
8use serde::Serialize;
9
10use crate::{utils::process_info::get_running_client, Error, RequestClient};
11
12pub struct LcuClient {
14 url: String,
15 auth_header: String,
16}
17
18#[cfg(feature = "batched")]
19pub mod batch {
20 use crate::rest::LcuClient;
21 use crate::{Error, RequestClient};
22 use futures_util::StreamExt;
23 use serde::de::DeserializeOwned;
24 use std::borrow::Cow;
25
26 pub enum RequestType<'a> {
28 Delete,
29 Get,
30 Patch(Option<&'a dyn erased_serde::Serialize>),
31 Post(Option<&'a dyn erased_serde::Serialize>),
32 Put(Option<&'a dyn erased_serde::Serialize>),
33 }
34
35 pub struct Request<'a> {
38 pub request_type: RequestType<'a>,
39 pub endpoint: Cow<'static, str>,
40 }
41
42 impl<'a> Request<'a> {
43 pub fn new(request_type: RequestType<'a>, endpoint: impl Into<Cow<'static, str>>) -> Self {
45 Request {
46 request_type,
47 endpoint: endpoint.into(),
48 }
49 }
50
51 pub fn delete(endpoint: impl Into<Cow<'static, str>>) -> Self {
52 Self::new(RequestType::Delete, endpoint)
53 }
54
55 pub fn get(endpoint: impl Into<Cow<'static, str>>) -> Self {
56 Self::new(RequestType::Get, endpoint)
57 }
58
59 pub fn patch(
60 endpoint: impl Into<Cow<'static, str>>,
61 body: Option<&'a dyn erased_serde::Serialize>,
62 ) -> Self {
63 Self::new(RequestType::Patch(body), endpoint)
64 }
65
66 pub fn put(
67 endpoint: impl Into<Cow<'static, str>>,
68 body: Option<&'a dyn erased_serde::Serialize>,
69 ) -> Self {
70 Self::new(RequestType::Put(body), endpoint)
71 }
72
73 pub fn post(
74 endpoint: impl Into<Cow<'static, str>>,
75 body: Option<&'a dyn erased_serde::Serialize>,
76 ) -> Self {
77 Self::new(RequestType::Post(body), endpoint)
78 }
79 }
80
81 impl LcuClient {
82 pub async fn batched<'a, R>(
89 &self,
90 requests: &[Request<'a>],
91 buffer_size: usize,
92 request_client: &RequestClient,
93 ) -> Vec<Result<Option<R>, Error>>
94 where
95 R: DeserializeOwned,
96 {
97 futures_util::stream::iter(requests.iter().map(|request| async {
98 let endpoint = &*request.endpoint;
99 match &request.request_type {
100 RequestType::Delete => self.delete(endpoint, request_client).await,
101 RequestType::Get => self.get(endpoint, request_client).await,
102 RequestType::Patch(body) => self.patch(endpoint, *body, request_client).await,
103 RequestType::Post(body) => self.post(endpoint, *body, request_client).await,
104 RequestType::Put(body) => self.put(endpoint, *body, request_client).await,
105 }
106 }))
107 .buffered(buffer_size)
108 .collect()
109 .await
110 }
111 }
112
113 pub struct Builder;
114
115 mod hidden {
116 use crate::rest::batch::Request;
117 use crate::rest::LcuClient;
118 use crate::RequestClient;
119
120 pub struct WithClient<'a> {
121 pub(super) request_client: &'a RequestClient,
122 pub(super) requests: Vec<Request<'a>>,
123 }
124
125 pub struct WithBufferSize<'a> {
126 pub(super) request_client: &'a RequestClient,
127 pub(super) requests: Vec<Request<'a>>,
128 pub(super) buffer_size: usize,
129 }
130
131 pub struct WithLcuClient<'a> {
132 pub(super) request_client: &'a RequestClient,
133 pub(super) requests: Vec<Request<'a>>,
134 pub(super) buffer_size: usize,
135 pub(super) lcu_client: &'a LcuClient,
136 }
137 }
138
139 use crate::rest::batch::hidden::WithLcuClient;
140 use hidden::{WithBufferSize, WithClient};
141
142 impl Builder {
143 #[must_use]
144 pub fn new() -> Self {
145 Self
146 }
147
148 #[must_use]
149 pub fn with_client(self, request_client: &RequestClient) -> WithClient {
150 WithClient {
151 request_client,
152 requests: Vec::new(),
153 }
154 }
155
156 #[must_use]
157 pub fn with_client_and_capacity(
158 self,
159 request_client: &RequestClient,
160 capacity: usize,
161 ) -> WithClient {
162 WithClient {
163 request_client,
164 requests: Vec::with_capacity(capacity),
165 }
166 }
167 }
168
169 impl Default for Builder {
170 fn default() -> Self {
171 Self
172 }
173 }
174
175 impl<'a> WithClient<'a> {
176 pub fn request(mut self, request: Request<'a>) -> Self {
177 self.add_request(request);
178
179 self
180 }
181
182 pub fn add_request(&mut self, request: Request<'a>) {
183 self.requests.push(request);
184 }
185
186 pub fn with_buffer_size(self, buffer_size: usize) -> WithBufferSize<'a> {
187 WithBufferSize {
188 requests: self.requests,
189 request_client: self.request_client,
190 buffer_size,
191 }
192 }
193 }
194
195 impl<'a> WithBufferSize<'a> {
196 pub fn with_lcu_client(self, lcu_client: &'a LcuClient) -> WithLcuClient<'a> {
197 WithLcuClient {
198 requests: self.requests,
199 request_client: self.request_client,
200 buffer_size: self.buffer_size,
201 lcu_client,
202 }
203 }
204 }
205
206 impl<'a> WithLcuClient<'a> {
207 pub async fn execute<R: DeserializeOwned>(self) -> Vec<Result<Option<R>, Error>> {
208 self.lcu_client
209 .batched(&self.requests, self.buffer_size, self.request_client)
210 .await
211 }
212 }
213}
214
215impl LcuClient {
216 pub fn new(force_lock_file: bool) -> Result<Self, Error> {
227 let (port, pass) = get_running_client(force_lock_file)?;
228
229 Ok(LcuClient {
230 url: port,
231 auth_header: pass,
232 })
233 }
234
235 #[must_use]
236 pub fn new_with_credentials(auth: &str, port: u16) -> LcuClient {
239 LcuClient {
240 url: format!("127.0.0.1:{port}"),
241 auth_header: format!(
242 "Basic {}",
243 crate::utils::process_info::ENCODER.encode(format!("riot:{auth}"))
244 ),
245 }
246 }
247
248 pub fn reconnect(&mut self, force_lock_file: bool) -> Result<(), Error> {
254 let (port, pass) = get_running_client(force_lock_file)?;
255 self.url = port;
256 self.auth_header = pass;
257 Ok(())
258 }
259
260 pub fn reconnect_with_credentials(&mut self, auth: &str, port: u16) {
262 let port = format!("127.0.0.1:{port}");
263 let pass = format!(
264 "Basic {}",
265 crate::utils::process_info::ENCODER.encode(format!("riot:{auth}"))
266 );
267 self.url = port;
268 self.auth_header = pass;
269 }
270
271 #[must_use]
272 pub fn url(&self) -> &str {
274 &self.url
275 }
276
277 #[must_use]
278 pub fn auth_header(&self) -> &str {
280 &self.auth_header
281 }
282
283 pub async fn delete<R: DeserializeOwned>(
288 &self,
289 endpoint: impl AsRef<str>,
290 request_client: &RequestClient,
291 ) -> Result<Option<R>, Error> {
292 self.lcu_request::<(), R>(endpoint.as_ref(), "DELETE", None, request_client)
293 .await
294 }
295
296 pub async fn get<R: DeserializeOwned>(
307 &self,
308 endpoint: impl AsRef<str>,
309 request_client: &RequestClient,
310 ) -> Result<Option<R>, Error> {
311 self.lcu_request::<(), R>(endpoint.as_ref(), "GET", None, request_client)
312 .await
313 }
314
315 pub async fn head<S>(
320 &self,
321 endpoint: impl AsRef<str>,
322 request_client: &RequestClient,
323 ) -> Result<hyper::Response<hyper::body::Incoming>, Error>
324 {
325 request_client.raw_request_template::<()>(&self.url, endpoint.as_ref(), "HEAD", None, Some(&self.auth_header))
326 .await
327 }
328
329 pub async fn patch<T, R>(
334 &self,
335 endpoint: impl AsRef<str>,
336 body: Option<T>,
337 request_client: &RequestClient,
338 ) -> Result<Option<R>, Error>
339 where
340 T: Serialize,
341 R: DeserializeOwned,
342 {
343 self.lcu_request(endpoint.as_ref(), "PATCH", body, request_client)
344 .await
345 }
346
347 pub async fn post<T, R>(
352 &self,
353 endpoint: impl AsRef<str>,
354 body: Option<T>,
355 request_client: &RequestClient,
356 ) -> Result<Option<R>, Error>
357 where
358 T: Serialize,
359 R: DeserializeOwned,
360 {
361 self.lcu_request(endpoint.as_ref(), "POST", body, request_client)
362 .await
363 }
364
365 pub async fn put<T, R>(
370 &self,
371 endpoint: impl AsRef<str>,
372 body: Option<T>,
373 request_client: &RequestClient,
374 ) -> Result<Option<R>, Error>
375 where
376 T: Serialize,
377 R: DeserializeOwned,
378 {
379 self.lcu_request(endpoint.as_ref(), "PUT", body, request_client)
380 .await
381 }
382
383 pub async fn schema(remote: &'static str) -> Result<types::Schema, Error> {
391 let uri = Uri::from_static(remote);
392 let https = hyper_rustls::HttpsConnectorBuilder::new()
394 .with_native_roots()
395 .map_err(Error::StdIo)?
396 .https_only()
397 .enable_http1()
398 .build();
399 let client =
400 hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new())
401 .build::<_, http_body_util::Full<hyper::body::Bytes>>(https);
402 let mut request = client.get(uri).await.map_err(Error::HyperClientError)?;
403 let tmp = request.body_mut();
404 let body = tmp.collect().await.map_err(Error::HyperError)?.to_bytes();
405 serde_json::from_slice(&body).map_err(Error::SerdeJsonError)
406 }
407
408 pub async fn lcu_request<T: Serialize, R: DeserializeOwned>(
414 &self,
415 endpoint: &str,
416 method: &str,
417 body: Option<T>,
418 request_client: &RequestClient,
419 ) -> Result<Option<R>, Error> {
420 request_client
421 .request_template(
422 &self.url,
423 endpoint,
424 method,
425 body,
426 Some(&self.auth_header),
427 |bytes| {
428 let body = if bytes.is_empty() {
429 None
430 } else {
431 Some(serde_json::from_slice(&bytes)?)
432 };
433
434 Ok(body)
435 },
436 )
437 .await
438 }
439}
440
441#[cfg(feature = "batched")]
442#[cfg(test)]
443mod tests {
444 use crate::{rest::LcuClient, RequestClient};
445
446 #[tokio::test]
447 async fn batch_test() {
448 use crate::rest::{
449 batch::{Request, RequestType},
450 LcuClient,
451 };
452
453 let page = serde_json::json!(
454 {
455 "blocks": [
456 {
457 "items": [
458 {
459 "count": 1,
460 "id": "3153"
461 },
462 ],
463 "type": "Final Build"
464 }
465 ],
466 "title": "Test Build",
467 }
468 );
469 let client = RequestClient::new();
470
471 let lcu_client = LcuClient::new(false).unwrap();
472
473 let request: serde_json::Value = lcu_client
474 .get("/lol-summoner/v1/current-summoner", &client)
475 .await
476 .unwrap()
477 .unwrap();
478
479 let id = &request["summonerId"];
480
481 let endpoint = format!("/lol-item-sets/v1/item-sets/{id}/sets");
482
483 let mut json: serde_json::Value = lcu_client
484 .get(endpoint.as_str(), &client)
485 .await
486 .unwrap()
487 .unwrap();
488
489 json["itemSets"].as_array_mut().unwrap().push(page);
490
491 let req = Request {
492 request_type: RequestType::Put(Some(&json)),
493 endpoint: format!("/lol-item-sets/v1/item-sets/{id}/sets").into(),
494 };
495
496 let result = lcu_client
497 .batched::<serde_json::Value>(&[req], 1, &client)
498 .await;
499
500 println!("{result:?}");
501
502 let a = lcu_client
503 .put::<_, serde_json::Value>(
504 format!("/lol-item-sets/v1/item-sets/{id}/sets"),
505 Some(json),
506 &client,
507 )
508 .await;
509 println!("{a:?}");
510 }
511
512 #[tokio::test]
513 async fn test_schema_des() {
514 let _schema = LcuClient::schema(
515 "https://raw.githubusercontent.com/dysolix/hasagi-types/main/swagger.json",
516 )
517 .await
518 .unwrap();
519 }
520}