melvin_ob/http_handler/http_request/
request_common.rs1use super::response_common::{HTTPResponseType, ResponseError};
2use crate::http_handler::{HTTPError, http_client::HTTPClient};
3use std::{fmt::Debug, io::ErrorKind};
4use std::collections::HashMap;
5use std::path::PathBuf;
6use strum_macros::Display;
7
8pub(crate) trait HTTPRequestType {
13 type Response: HTTPResponseType;
15
16 fn endpoint(&self) -> &str;
18
19 fn request_method(&self) -> HTTPRequestMethod;
21
22 fn header_params(&self) -> reqwest::header::HeaderMap {
24 reqwest::header::HeaderMap::default()
25 }
26
27 fn query_params(&self) -> HashMap<&str, String> {
29 HashMap::new()
30 }
31
32 fn get_request_base(&self, client: &HTTPClient) -> reqwest::RequestBuilder {
40 let compound_url = format!("{}{}", client.url(), self.endpoint());
41 match self.request_method() {
42 HTTPRequestMethod::Get => client.client().get(compound_url),
43 HTTPRequestMethod::Post => client.client().post(compound_url),
44 HTTPRequestMethod::Put => client.client().put(compound_url),
45 HTTPRequestMethod::Delete => client.client().delete(compound_url),
46 }
47 }
48}
49
50
51#[derive(Debug)]
53pub(crate) enum HTTPRequestMethod {
54 Get,
56 Post,
58 Put,
60 Delete,
62}
63
64
65#[derive(Debug, Display)]
67pub(crate) enum RequestError {
68 NotFound,
70 FailedToOpen,
72 Unknown,
74}
75
76impl std::error::Error for RequestError {}
77
78impl From<std::io::Error> for RequestError {
79 fn from(value: std::io::Error) -> Self {
81 match value.kind() {
82 ErrorKind::NotFound => RequestError::NotFound,
83 ErrorKind::PermissionDenied => RequestError::FailedToOpen,
84 _ => RequestError::Unknown,
85 }
86 }
87}
88
89pub(crate) trait JSONBodyHTTPRequestType: HTTPRequestType {
93 type Body: serde::Serialize;
95
96 fn body(&self) -> &Self::Body;
98
99 fn header_params_with_content_type(&self) -> reqwest::header::HeaderMap {
101 let mut headers = self.header_params();
102 headers.append(
103 "Content-Type",
104 reqwest::header::HeaderValue::from_static("application/json"),
105 );
106 headers
107 }
108
109 async fn send_request(
117 &self,
118 client: &HTTPClient,
119 ) -> Result<<Self::Response as HTTPResponseType>::ParsedResponseType, HTTPError> {
120 let response = self
121 .get_request_base(client)
122 .headers(self.header_params_with_content_type())
123 .query(&self.query_params())
124 .json(&self.body())
125 .send()
126 .await;
127 let resp = response.map_err(ResponseError::from);
128 Self::Response::read_response(resp.map_err(HTTPError::HTTPResponseError)?)
129 .await
130 .map_err(HTTPError::HTTPResponseError)
131 }
132}
133
134
135pub(crate) trait NoBodyHTTPRequestType: HTTPRequestType {
137 async fn send_request(
145 &self,
146 client: &HTTPClient,
147 ) -> Result<<Self::Response as HTTPResponseType>::ParsedResponseType, HTTPError> {
148 let response = self
149 .get_request_base(client)
150 .headers(self.header_params())
151 .query(&self.query_params())
152 .send()
153 .await;
154 let resp = response.map_err(ResponseError::from);
155 Self::Response::read_response(resp.map_err(HTTPError::HTTPResponseError)?)
156 .await
157 .map_err(HTTPError::HTTPResponseError)
158 }
159}
160
161pub(crate) trait MultipartBodyHTTPRequestType: HTTPRequestType {
165 async fn body(&self) -> Result<reqwest::multipart::Form, RequestError> {
170 let file_part = reqwest::multipart::Part::file(self.image_path()).await?;
171 Ok(reqwest::multipart::Form::new().part("image", file_part))
172 }
173
174 fn image_path(&self) -> &PathBuf;
176
177 async fn send_request(
185 &self,
186 client: &HTTPClient,
187 ) -> Result<<Self::Response as HTTPResponseType>::ParsedResponseType, HTTPError> {
188 let response = self
189 .get_request_base(client)
190 .headers(self.header_params())
191 .query(&self.query_params())
192 .multipart(self.body().await.map_err(HTTPError::HTTPRequestError)?)
193 .send()
194 .await;
195 let resp = response.map_err(ResponseError::from);
196 Self::Response::read_response(resp.map_err(HTTPError::HTTPResponseError)?)
197 .await
198 .map_err(HTTPError::HTTPResponseError)
199 }
200}
201
202pub(super) fn bool_to_string(value: bool) -> &'static str {
212 if value { "true" } else { "false" }
213}