coman/core/
http_client.rs1use crate::core::errors::HttpError;
7use crate::core::http_request::HttpRequest;
8use crate::core::http_response::HttpResponse;
9use std::time::Duration;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum HttpMethod {
14 Get,
15 Post,
16 Put,
17 Delete,
18 Patch,
19}
20
21impl std::fmt::Display for HttpMethod {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match self {
24 HttpMethod::Get => write!(f, "GET"),
25 HttpMethod::Post => write!(f, "POST"),
26 HttpMethod::Put => write!(f, "PUT"),
27 HttpMethod::Delete => write!(f, "DELETE"),
28 HttpMethod::Patch => write!(f, "PATCH"),
29 }
30 }
31}
32
33impl From<crate::models::collection::Method> for HttpMethod {
34 fn from(method: crate::models::collection::Method) -> Self {
35 match method {
36 crate::models::collection::Method::Get => HttpMethod::Get,
37 crate::models::collection::Method::Post => HttpMethod::Post,
38 crate::models::collection::Method::Put => HttpMethod::Put,
39 crate::models::collection::Method::Delete => HttpMethod::Delete,
40 crate::models::collection::Method::Patch => HttpMethod::Patch,
41 }
42 }
43}
44
45pub type HttpResult<T> = Result<T, HttpError>;
47
48#[derive(Debug, Clone, Default)]
50pub struct HttpClient {
51 default_headers: Vec<(String, String)>,
52 timeout: Option<Duration>,
53 follow_redirects: bool,
54}
55
56impl HttpClient {
57 pub fn new() -> Self {
59 Self::default()
60 }
61
62 pub fn with_default_headers(mut self, headers: Vec<(String, String)>) -> Self {
64 self.default_headers = headers;
65 self
66 }
67
68 pub fn with_timeout(mut self, timeout: Duration) -> Self {
70 self.timeout = Some(timeout);
71 self
72 }
73
74 pub fn with_follow_redirects(mut self, follow: bool) -> Self {
76 self.follow_redirects = follow;
77 self
78 }
79
80 pub fn get(&self, url: &str) -> HttpRequest {
82 self.request(HttpMethod::Get, url)
83 }
84
85 pub fn post(&self, url: &str) -> HttpRequest {
87 self.request(HttpMethod::Post, url)
88 }
89
90 pub fn put(&self, url: &str) -> HttpRequest {
92 self.request(HttpMethod::Put, url)
93 }
94
95 pub fn delete(&self, url: &str) -> HttpRequest {
97 self.request(HttpMethod::Delete, url)
98 }
99
100 pub fn patch(&self, url: &str) -> HttpRequest {
102 self.request(HttpMethod::Patch, url)
103 }
104
105 pub fn request(&self, method: HttpMethod, url: &str) -> HttpRequest {
107 let mut request = HttpRequest::new(method, url)
108 .headers(self.default_headers.clone())
109 .follow_redirects(self.follow_redirects);
110
111 if let Some(timeout) = self.timeout {
112 request = request.timeout(timeout);
113 }
114
115 request
116 }
117
118 pub async fn execute_endpoint(
120 &self,
121 manager: &crate::core::collection_manager::CollectionManager,
122 collection: &str,
123 endpoint: &str,
124 ) -> HttpResult<HttpResponse> {
125 let col = manager
126 .get_collection(collection)
127 .map_err(|e| HttpError::Other(e.to_string()))?;
128 let req = manager
129 .get_endpoint(collection, endpoint)
130 .map_err(|e| HttpError::Other(e.to_string()))?;
131
132 let url = format!("{}{}", col.url, req.endpoint);
133 let headers = manager
134 .get_endpoint_headers(collection, endpoint)
135 .map_err(|e| HttpError::Other(e.to_string()))?;
136
137 let method: HttpMethod = req.method.into();
138
139 let mut request = HttpRequest::new(method, &url)
140 .headers(headers)
141 .follow_redirects(self.follow_redirects);
142
143 if let Some(body) = &req.body {
144 request = request.body(body);
145 }
146
147 if let Some(timeout) = self.timeout {
148 request = request.timeout(timeout);
149 }
150
151 request.send().await
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use std::collections::HashMap;
158
159 use crate::core::utils::build_header_map;
160
161 use super::*;
162
163 #[test]
164 fn test_http_method_display() {
165 assert_eq!(HttpMethod::Get.to_string(), "GET");
166 assert_eq!(HttpMethod::Post.to_string(), "POST");
167 assert_eq!(HttpMethod::Put.to_string(), "PUT");
168 assert_eq!(HttpMethod::Delete.to_string(), "DELETE");
169 assert_eq!(HttpMethod::Patch.to_string(), "PATCH");
170 }
171
172 #[test]
173 fn test_http_response_status_checks() {
174 let response = HttpResponse {
175 version: "HTTP/1.1".to_string(),
176 status: 200,
177 status_text: "OK".to_string(),
178 headers: HashMap::new(),
179 body: String::new(),
180 elapsed_ms: 0,
181 url: String::new(),
182 };
183
184 assert!(response.is_success());
185 assert!(!response.is_redirect());
186 assert!(!response.is_client_error());
187 assert!(!response.is_server_error());
188 }
189
190 #[test]
191 fn test_build_header_map() {
192 let headers = vec![
193 ("Content-Type".to_string(), "application/json".to_string()),
194 ("Authorization".to_string(), "Bearer token".to_string()),
195 ];
196
197 let header_map = build_header_map(&headers);
198 assert_eq!(header_map.len(), 2);
199 }
200}