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}