1use crate::error::app_error::AppError;
2use crate::http::parser::{JsonParser, ResponseParser, StringParser};
3use reqwest::{Client, Error, Method, RequestBuilder, Response};
4use serde::de::DeserializeOwned;
5use serde::{Deserialize, Serialize};
6use std::any::TypeId;
7use std::collections::HashMap;
8use std::marker::PhantomData;
9use std::time::Duration;
10
11#[derive(Debug, Serialize, Deserialize, Clone)]
12pub struct Headers {
13 pub header: Option<HashMap<String, String>>,
14 pub auth: Option<Auth>,
15}
16#[derive(Debug, Serialize, Deserialize, Clone)]
17pub struct Auth {
18 pub username: String,
19 pub password: String,
20 pub token: String,
21}
22
23#[derive(Debug)]
24pub enum HttpMethod {
25 GET,
26 POST,
27 PUT,
28 DELETE,
29 HEAD,
30}
31
32async fn parse_response<T>(response: Response) -> Result<T, AppError>
34where
35 T: for<'a> Deserialize<'a> + std::fmt::Debug + 'static + From<String>,
36{
37 let body_str = response.text().await?;
38
39 if TypeId::of::<T>() == TypeId::of::<String>() {
41 Ok(<StringParser as ResponseParser<T>>::parse(&body_str)?)
42 } else {
43 Ok(<JsonParser<T> as ResponseParser<T>>::parse(&body_str)?)
44 }
45}
46
47pub async fn do_get<T, E>(
58 base_url: &str,
59 params: Option<HashMap<String, String>>,
60 headers: Option<Headers>,
61 show_debug: Option<bool>,
62) -> Result<T, E>
63where
64 T: for<'a> Deserialize<'a> + std::fmt::Debug + 'static + From<String>,
65 E: std::fmt::Debug
66 + std::fmt::Display
67 + From<AppError>
68 + From<reqwest::Error>
69 + From<serde_json::Error>,
70{
71 let response = request_build(base_url, Method::GET, params, "", headers, show_debug).await?;
73 if response.status().is_success() {
74 let result: T = parse_response(response).await?;
76 tracing::info!("[do_get] http result: {:?}", result);
77 return Ok(result);
78 }
79 Err(AppError::from(response.text().await?).into())
80}
81
82pub async fn do_post_with_params<T, E>(
93 base_url: &str,
94 params: Option<HashMap<String, String>>,
95 headers: Option<Headers>,
96 show_debug: Option<bool>,
97) -> Result<T, E>
98where
99 T: for<'a> Deserialize<'a> + std::fmt::Debug + 'static + From<String>,
100 E: std::fmt::Debug
101 + std::fmt::Display
102 + From<AppError>
103 + From<reqwest::Error>
104 + From<serde_json::Error>,
105{
106 let response = request_build(base_url, Method::POST, params, "", headers, show_debug).await?;
108 let result: T = parse_response(response).await?;
110 tracing::info!("[do_post_with_params] http result: {:?}", result);
112 Ok(result)
113}
114
115pub async fn do_post_with_body<T, E>(
127 base_url: &str,
128 params: Option<HashMap<String, String>>,
129 body: &str,
130 headers: Option<Headers>,
131 show_debug: Option<bool>,
132) -> Result<T, E>
133where
134 T: for<'a> Deserialize<'a> + std::fmt::Debug + 'static + From<String>,
135 E: std::fmt::Debug
136 + std::fmt::Display
137 + From<AppError>
138 + From<reqwest::Error>
139 + From<serde_json::Error>,
140{
141 let response = request_build(base_url, Method::POST, params, body, headers, show_debug).await?;
143 let result: T = parse_response(response).await?;
145 tracing::info!("[do_post_with_body] http result: {:?}", result);
147 Ok(result)
148}
149
150pub async fn do_something<T, E>(
151 base_url: &str,
152 method: Method,
153 params: Option<HashMap<String, String>>,
154 body: &str,
155 headers: Option<Headers>,
156 show_debug: Option<bool>,
157) -> Result<T, E>
158where
159 T: for<'a> Deserialize<'a> + std::fmt::Debug + 'static + From<String>,
160 E: std::fmt::Debug
161 + std::fmt::Display
162 + From<AppError>
163 + From<reqwest::Error>
164 + From<serde_json::Error>,
165{
166 let response = request_build(base_url, method.clone(), params, body, headers, show_debug).await?;
168 let result: T = parse_response(response).await?;
170 tracing::info!(
172 "[do_something_with_{:?}] http result: {:?}",
173 method.clone(),
174 result
175 );
176 Ok(result)
177}
178async fn request_build(
179 base_url: &str,
180 method: Method,
181 params: Option<HashMap<String, String>>,
182 body: &str,
183 headers: Option<Headers>,
184 show_debug: Option<bool>,
185) -> Result<Response, AppError> {
186 tracing::info!(
189 "request url: {}, method: {}, params: {:?}, body ignored",
190 base_url,
191 method.clone(),
192 params.clone()
193 );
194 if show_debug.is_some() && show_debug.unwrap() {
195 tracing::info!("request body: {:?}", body.clone());
196 tracing::info!("request header: {:?}", headers.clone())
197 }
198 let client = Client::builder().timeout(Duration::from_secs(60)).build()?;
200 let mut request_builder = client.request(method.clone(), base_url);
202 if !params.is_none() && !params.clone().unwrap().is_empty() {
204 request_builder = request_builder.query(¶ms);
205 }
206 if headers.is_some() {
208 let headers = headers.unwrap();
209 if headers.header.is_some() {
211 request_builder = set_headers(headers.header.unwrap(), request_builder).await;
212 }
213 if headers.auth.is_some() {
215 request_builder = set_auth(headers.auth.unwrap(), request_builder).await;
216 }
217 }
218
219 if !body.is_empty() {
221 request_builder = request_builder.body(body.to_string());
222 }
223 Ok(request_builder.send().await?)
224}
225
226async fn set_url(base_url: &str, params: &[(&str, &str)]) -> String {
227 let mut url = base_url.to_string();
228 if !params.is_empty() {
229 let str_params = query_to_string(params).await;
230 url = format!("{}?{}", url, str_params);
231 }
232 url
233}
234
235async fn query_to_string(params: &[(&str, &str)]) -> String {
236 let mut query_string = String::new();
237 for (key, value) in params {
238 query_string.push_str(&format!("{}={}&", key, value));
239 }
240 query_string.pop();
241
242 query_string
243}
244
245async fn set_headers(
246 headers: HashMap<String, String>,
247 mut request_builder: RequestBuilder,
248) -> RequestBuilder {
249 let owned_map: Vec<(String, String)> = headers
250 .iter()
251 .map(|(k, v)| (k.to_string(), v.to_string()))
252 .collect();
253 for (key, value) in owned_map {
255 request_builder = request_builder.header(key, value);
256 }
257 request_builder
258}
259
260async fn set_auth(auth: Auth, mut request_builder: RequestBuilder) -> RequestBuilder {
261 let username = auth.username;
262 let password = auth.password;
263 let token = auth.token;
264 if !username.is_empty() {
265 if !password.is_empty() {
266 request_builder = request_builder.basic_auth(username, Some(password));
267 } else if !token.is_empty() {
268 request_builder = request_builder.bearer_auth(token);
269 }
270 }
271 request_builder
272}