1use bytes::Bytes;
6use http::{HeaderMap, HeaderValue, Method, Request};
7use http_body_util::Full;
8use serde::Serialize;
9use serde_json::Value;
10use std::collections::HashMap;
11
12use crate::client::ClientError;
13
14pub struct APIRequestFactory {
16 default_format: String,
17 default_headers: HeaderMap,
18}
19
20impl APIRequestFactory {
21 pub fn new() -> Self {
32 Self {
33 default_format: "json".to_string(),
34 default_headers: HeaderMap::new(),
35 }
36 }
37 pub fn with_format(mut self, format: impl Into<String>) -> Self {
38 self.default_format = format.into();
39 self
40 }
41 pub fn with_header(
42 mut self,
43 name: impl AsRef<str>,
44 value: impl AsRef<str>,
45 ) -> Result<Self, ClientError> {
46 let header_name: http::header::HeaderName = name.as_ref().parse().map_err(|_| {
47 ClientError::RequestFailed(format!("Invalid header name: {}", name.as_ref()))
48 })?;
49 self.default_headers
50 .insert(header_name, HeaderValue::from_str(value.as_ref())?);
51 Ok(self)
52 }
53 pub fn get(&self, path: &str) -> RequestBuilder {
65 RequestBuilder::new(Method::GET, path, &self.default_headers)
66 }
67 pub fn post(&self, path: &str) -> RequestBuilder {
81 RequestBuilder::new(Method::POST, path, &self.default_headers)
82 .with_format(&self.default_format)
83 }
84 pub fn put(&self, path: &str) -> RequestBuilder {
98 RequestBuilder::new(Method::PUT, path, &self.default_headers)
99 .with_format(&self.default_format)
100 }
101 pub fn patch(&self, path: &str) -> RequestBuilder {
115 RequestBuilder::new(Method::PATCH, path, &self.default_headers)
116 .with_format(&self.default_format)
117 }
118 pub fn delete(&self, path: &str) -> RequestBuilder {
130 RequestBuilder::new(Method::DELETE, path, &self.default_headers)
131 }
132 pub fn head(&self, path: &str) -> RequestBuilder {
144 RequestBuilder::new(Method::HEAD, path, &self.default_headers)
145 }
146 pub fn options(&self, path: &str) -> RequestBuilder {
158 RequestBuilder::new(Method::OPTIONS, path, &self.default_headers)
159 }
160 pub fn request(&self, method: Method, path: &str) -> RequestBuilder {
173 RequestBuilder::new(method, path, &self.default_headers)
174 }
175}
176
177impl Default for APIRequestFactory {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182
183pub struct RequestBuilder {
185 method: Method,
186 path: String,
187 headers: HeaderMap,
188 query_params: HashMap<String, String>,
189 body: Option<Bytes>,
190 format: String,
191 user: Option<Value>,
192}
193
194impl RequestBuilder {
195 pub fn new(method: Method, path: &str, default_headers: &HeaderMap) -> Self {
196 Self {
197 method,
198 path: path.to_string(),
199 headers: default_headers.clone(),
200 query_params: HashMap::new(),
201 body: None,
202 format: "json".to_string(),
203 user: None,
204 }
205 }
206 pub fn method(&self) -> Method {
207 self.method.clone()
208 }
209 pub fn path(&self) -> &str {
210 &self.path
211 }
212 pub fn with_format(mut self, format: &str) -> Self {
213 self.format = format.to_string();
214 self
215 }
216 pub fn header(mut self, name: &str, value: &str) -> Result<Self, ClientError> {
218 let header_name: http::header::HeaderName = name
219 .parse()
220 .map_err(|_| ClientError::RequestFailed(format!("Invalid header name: {}", name)))?;
221 self.headers
222 .insert(header_name, HeaderValue::from_str(value)?);
223 Ok(self)
224 }
225 pub fn query(mut self, key: &str, value: &str) -> Self {
226 self.query_params.insert(key.to_string(), value.to_string());
227 self
228 }
229 pub fn query_param(self, key: &str, value: &str) -> Self {
230 self.query(key, value)
231 }
232 pub fn json<T: Serialize>(mut self, data: &T) -> Result<Self, ClientError> {
245 let json = serde_json::to_vec(data)?;
246 self.body = Some(Bytes::from(json));
247 self.format = "json".to_string();
248 Ok(self)
249 }
250 pub fn form<T: Serialize>(mut self, data: &T) -> Result<Self, ClientError> {
263 let json_value = serde_json::to_value(data)?;
264 if let Value::Object(map) = json_value {
265 let form_data = map
266 .iter()
267 .map(|(k, v)| {
268 let value_str = match v {
269 Value::String(s) => s.clone(),
270 _ => v.to_string(),
271 };
272 format!(
273 "{}={}",
274 url::form_urlencoded::byte_serialize(k.as_bytes()).collect::<String>(),
275 url::form_urlencoded::byte_serialize(value_str.as_bytes())
276 .collect::<String>()
277 )
278 })
279 .collect::<Vec<_>>()
280 .join("&");
281 self.body = Some(Bytes::from(form_data));
282 self.format = "form".to_string();
283 Ok(self)
284 } else {
285 Err(ClientError::RequestFailed(
286 "Expected object for form data".to_string(),
287 ))
288 }
289 }
290 pub fn body(mut self, body: impl Into<Bytes>) -> Self {
301 self.body = Some(body.into());
302 self
303 }
304 pub fn force_authenticate(mut self, user: Value) -> Self {
317 self.user = Some(user);
318 self
319 }
320 pub fn build(self) -> Result<Request<Full<Bytes>>, ClientError> {
332 let mut url = self.path.clone();
333
334 if !self.query_params.is_empty() {
336 let query_string = self
337 .query_params
338 .iter()
339 .map(|(k, v)| {
340 format!(
341 "{}={}",
342 url::form_urlencoded::byte_serialize(k.as_bytes()).collect::<String>(),
343 url::form_urlencoded::byte_serialize(v.as_bytes()).collect::<String>()
344 )
345 })
346 .collect::<Vec<_>>()
347 .join("&");
348 url = format!("{}?{}", url, query_string);
349 }
350
351 let mut request = Request::builder().method(self.method).uri(url);
352
353 for (name, value) in self.headers.iter() {
355 request = request.header(name, value);
356 }
357
358 if self.body.is_some() {
360 let content_type = match self.format.as_str() {
361 "json" => "application/json",
362 "form" => "application/x-www-form-urlencoded",
363 _ => "application/octet-stream",
364 };
365 request = request.header("Content-Type", content_type);
366 }
367
368 if self.user.is_some() {
370 request = request.header("X-Test-User", "authenticated");
371 }
372
373 let body = self.body.unwrap_or_default();
375 let req = request.body(Full::new(body))?;
376
377 Ok(req)
378 }
379}