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