1use std::time::Duration;
9
10use reqwest::{Client, ClientBuilder, Method};
11use serde::{de::DeserializeOwned, Serialize};
12
13use crate::{auth::Credentials, error::LcuError};
14
15pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10);
17
18pub fn build_lcu_client() -> Result<Client, LcuError> {
25 Ok(ClientBuilder::new()
26 .danger_accept_invalid_certs(true)
27 .danger_accept_invalid_hostnames(true)
28 .timeout(DEFAULT_TIMEOUT)
29 .build()?)
30}
31
32async fn send<T, B>(
35 client: &Client,
36 credentials: &Credentials,
37 method: Method,
38 endpoint: &str,
39 body: Option<&B>,
40) -> Result<T, LcuError>
41where
42 T: DeserializeOwned,
43 B: Serialize + ?Sized,
44{
45 let url = format!("{}{}", credentials.lcu_base_url(), endpoint);
46 let mut req = client
47 .request(method, &url)
48 .header("Authorization", credentials.basic_auth())
49 .header("Accept", "application/json");
50 if let Some(b) = body {
51 req = req.json(b);
52 }
53 let resp = req.send().await?;
54 let status = resp.status();
55 if !status.is_success() {
56 let body = resp.text().await.unwrap_or_default();
57 return Err(LcuError::Status {
58 code: status.as_u16(),
59 body,
60 });
61 }
62 Ok(resp.json::<T>().await?)
63}
64
65pub async fn lcu_request<T: DeserializeOwned>(
68 client: &Client,
69 credentials: &Credentials,
70 method: Method,
71 endpoint: &str,
72) -> Result<T, LcuError> {
73 send::<T, ()>(client, credentials, method, endpoint, None).await
74}
75
76pub async fn lcu_request_with_body<T, B>(
78 client: &Client,
79 credentials: &Credentials,
80 method: Method,
81 endpoint: &str,
82 body: &B,
83) -> Result<T, LcuError>
84where
85 T: DeserializeOwned,
86 B: Serialize + ?Sized,
87{
88 send(client, credentials, method, endpoint, Some(body)).await
89}
90
91pub async fn lcu_get<T: DeserializeOwned>(
105 client: &Client,
106 credentials: &Credentials,
107 endpoint: &str,
108) -> Result<T, LcuError> {
109 lcu_request(client, credentials, Method::GET, endpoint).await
110}
111
112pub async fn lcu_post<T, B>(
117 client: &Client,
118 credentials: &Credentials,
119 endpoint: &str,
120 body: &B,
121) -> Result<T, LcuError>
122where
123 T: DeserializeOwned,
124 B: Serialize + ?Sized,
125{
126 lcu_request_with_body(client, credentials, Method::POST, endpoint, body).await
127}
128
129pub async fn lcu_delete<T: DeserializeOwned>(
131 client: &Client,
132 credentials: &Credentials,
133 endpoint: &str,
134) -> Result<T, LcuError> {
135 lcu_request(client, credentials, Method::DELETE, endpoint).await
136}
137
138pub fn parse_marketing_version(raw: &str) -> Option<String> {
146 let parts: Vec<&str> = raw.split('.').collect();
147 if parts.len() < 2 {
148 return None;
149 }
150 let internal_major: u32 = parts[0].parse().ok()?;
151 let minor = parts[1];
152 Some(format!("{}.{:0>2}", internal_major + 10, minor))
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn version_parsing() {
161 assert_eq!(parse_marketing_version("4.21.614.6789"), Some("14.21".into()));
162 assert_eq!(parse_marketing_version("4.3.614.6789"), Some("14.03".into()));
163 assert_eq!(parse_marketing_version("bad"), None);
164 assert_eq!(parse_marketing_version(""), None);
165 }
166}