clientix_core/client/
request.rs

1use std::collections::HashMap;
2use std::time::Duration;
3use base64::Engine;
4use base64::prelude::BASE64_STANDARD;
5use http::{HeaderMap, HeaderName, HeaderValue};
6use http::header::{AUTHORIZATION, CONTENT_TYPE};
7use serde::Serialize;
8use crate::client::response::{ClientixError, ClientixErrorData, ClientixResult};
9use crate::core::headers::content_type::ContentType;
10
11#[derive(Clone, Debug, Default)]
12pub struct RequestConfig {
13    path: String,
14    headers: HeaderMap,
15    queries: Vec<(String, String)>,
16    body: Option<String>,
17    timeout: Option<Duration>
18}
19
20pub trait ClientixRequestBuilder {
21    
22    fn config(&mut self) -> &mut RequestConfig;
23    
24    fn result(&mut self) -> &mut ClientixResult<()>;
25    
26    fn path(mut self, path: &str) -> Self where Self: Sized {
27        self.config().set_path(path);
28        self
29    }
30
31    fn query(mut self, key: &str, value: &str) -> Self where Self: Sized {
32        self.config().add_query(key, value);
33        self
34    }
35
36    fn queries(mut self, queries: HashMap<String, String>) -> Self where Self: Sized {
37        for (key, value) in queries {
38            self = self.query(key.as_str(), value.as_str());
39        }
40
41        self
42    }
43
44    fn header(mut self, key: &str, value: &str) -> Self where Self: Sized {
45        self.config().set_header(key, value, false);
46        self
47    }
48
49    fn headers(mut self, headers: HashMap<String, String>) -> Self where Self: Sized {
50        for (key, value) in headers {
51            self = self.header(key.as_str(), value.as_str());
52        }
53
54        self
55    }
56
57    fn basic_auth(mut self, username: &str, password: &str) -> Self where Self: Sized {
58        self.config().basic_auth(username, password);
59        self
60    }
61
62    fn bearer_auth(mut self, token: &str) -> Self where Self: Sized {
63        self.config().bearer_auth(token);
64        self
65    }
66
67    fn body<T: Serialize>(mut self, body: T, content_type: ContentType) -> Self where Self: Sized {
68        *self.result() = self.config().set_body(body, content_type);
69        self
70    }
71    
72}
73
74impl RequestConfig {
75    
76    pub fn new() -> Self {
77        RequestConfig {
78            path: Default::default(),
79            headers: Default::default(),
80            queries: Default::default(),
81            body: None,
82            timeout: None,
83        }
84    }
85    
86    pub fn get_path(&self) -> &String { 
87        &self.path 
88    }
89    
90    pub fn set_path(&mut self, path: &str) {
91        self.path = path.to_string();
92    }
93    
94    pub fn get_queries(&self) -> &Vec<(String, String)> {
95        &self.queries
96    }
97    
98    pub fn add_query(&mut self, key: &str, value: &str) {
99        self.queries.push((key.to_string(), value.to_string()));
100    }
101
102    pub fn add_queries(&mut self, queries: HashMap<String, String>) {
103        for (key, value) in queries {
104            self.queries.push((key, value));
105        }
106    }
107
108    pub fn set_queries(&mut self, queries: HashMap<String, String>) {
109        self.queries.clear();
110
111        for (key, value) in queries {
112            self.queries.push((key, value));
113        }
114    }
115    
116    pub fn get_headers(&self) -> &HeaderMap {
117        &self.headers
118    }
119    
120    pub fn set_header(&mut self, key: &str, value: &str, sensitive: bool) {
121        let header_name = if let Ok(name) = HeaderName::from_bytes(key.as_bytes()) {
122            name
123        } else {
124            return;
125        };
126
127        let mut header_value = if let Ok(value) = HeaderValue::from_str(&value) {
128            value
129        } else {
130            return;
131        };
132
133        header_value.set_sensitive(sensitive);
134
135        self.headers.insert(header_name, header_value);
136    }
137
138    pub fn set_headers(&mut self, headers: HashMap<String, String>) {
139        for (key, value) in headers {
140            self.set_header(key.as_str(), value.as_str(), false);
141        }
142    }
143
144    pub fn basic_auth(&mut self, username: &str, password: &str) {
145        let basic_token = format!("Basic {}", BASE64_STANDARD.encode(format!("{username}:{password}")));
146        self.set_header(AUTHORIZATION.as_str(), basic_token.as_str(), true);
147    }
148
149    pub fn bearer_auth(&mut self, token: &str) {
150        self.set_header(AUTHORIZATION.as_str(), format!("Bearer {}", token).as_str(), true);
151    }
152
153    pub fn get_body(&self) -> &Option<String> {
154        &self.body
155    }
156    
157    pub fn set_body<T: Serialize>(&mut self, body: T, content_type: ContentType) -> ClientixResult<()> {
158        match content_type {
159            ContentType::ApplicationJson => self.set_json_body(body),
160            ContentType::ApplicationXWwwFormUrlEncoded => self.set_form_body(body),
161            ContentType::ApplicationXml => self.set_xml_body(body),
162            _ => Err(ClientixError::InvalidRequest(
163                ClientixErrorData::builder()
164                    .message(format!("invalid content type: {:?}", content_type).as_str())
165                    .build(), 
166                None
167            ))
168        }
169    }
170
171    fn set_json_body<T: Serialize>(&mut self, body: T) -> ClientixResult<()> {
172        match serde_json::to_string(&body) {
173            Ok(body) => {
174                self.body = Some(body);
175                self.headers.insert(CONTENT_TYPE, ContentType::ApplicationJson.try_into().unwrap());
176                Ok(())
177            },
178            Err(err) => Err(ClientixError::InvalidRequest(Default::default(), Some(err.into())))
179        }
180    }
181
182    fn set_xml_body<T: Serialize>(&mut self, body: T) -> ClientixResult<()> {
183        match serde_xml_rs::to_string(&body) {
184            Ok(body) => {
185                self.body = Some(body);
186                self.headers.insert(CONTENT_TYPE, ContentType::ApplicationXml.try_into().unwrap());
187                Ok(())
188            },
189            Err(err) => Err(ClientixError::InvalidRequest(Default::default(), Some(err.into())))
190        }
191    }
192
193    fn set_form_body<T: Serialize>(&mut self, body: T) -> ClientixResult<()> {
194        match serde_urlencoded::to_string(&body) {
195            Ok(body) => {
196                self.body = Some(body);
197                self.headers.insert(CONTENT_TYPE, ContentType::ApplicationXWwwFormUrlEncoded.try_into().unwrap());
198                Ok(())
199            },
200            Err(err) => Err(ClientixError::InvalidRequest(Default::default(), Some(err.into())))
201        }
202    }
203    
204    pub fn get_timeout(&self) -> Option<Duration> {
205        self.timeout
206    }
207    
208    pub fn set_timeout(&mut self, timeout: Duration) {
209        self.timeout = Some(timeout);
210    }
211    
212}