1use std::collections::{BTreeMap, HashMap};
5use std::fmt::Display;
6
7use bytes::Bytes;
8use chrono::{DateTime, FixedOffset};
9use maplit::hashmap;
10
11use crate::headers::HeaderValue;
12
13#[derive(Debug, Clone, PartialEq)]
15pub struct WebmachineRequest {
16 pub request_path: String,
18 pub base_path: String,
20 pub method: String,
22 pub headers: HashMap<String, Vec<HeaderValue>>,
24 pub body: Option<Bytes>,
26 pub query: HashMap<String, Vec<String>>
28}
29
30impl Default for WebmachineRequest {
31 fn default() -> WebmachineRequest {
33 WebmachineRequest {
34 request_path: "/".to_string(),
35 base_path: "/".to_string(),
36 method: "GET".to_string(),
37 headers: HashMap::new(),
38 body: None,
39 query: HashMap::new()
40 }
41 }
42}
43
44impl WebmachineRequest {
45 pub fn content_type(&self) -> String {
48 match self.headers.keys().find(|k| k.to_uppercase() == "CONTENT-TYPE") {
49 Some(header) => match self.headers.get(header).unwrap().first() {
50 Some(value) => value.clone().value,
51 None => "application/json".to_string()
52 },
53 None => "application/json".to_string()
54 }
55 }
56
57 pub fn is_put_or_post(&self) -> bool {
59 ["PUT", "POST"].contains(&self.method.to_uppercase().as_str())
60 }
61
62 pub fn is_get_or_head(&self) -> bool {
64 ["GET", "HEAD"].contains(&self.method.to_uppercase().as_str())
65 }
66
67 pub fn is_get(&self) -> bool {
69 self.method.to_uppercase() == "GET"
70 }
71
72 pub fn is_options(&self) -> bool {
74 self.method.to_uppercase() == "OPTIONS"
75 }
76
77 pub fn is_put(&self) -> bool {
79 self.method.to_uppercase() == "PUT"
80 }
81
82 pub fn is_post(&self) -> bool {
84 self.method.to_uppercase() == "POST"
85 }
86
87 pub fn is_delete(&self) -> bool {
89 self.method.to_uppercase() == "DELETE"
90 }
91
92 pub fn has_accept_header(&self) -> bool {
94 self.has_header("ACCEPT")
95 }
96
97 pub fn accept(&self) -> Vec<HeaderValue> {
99 self.find_header("ACCEPT")
100 }
101
102 pub fn has_accept_language_header(&self) -> bool {
104 self.has_header("ACCEPT-LANGUAGE")
105 }
106
107 pub fn accept_language(&self) -> Vec<HeaderValue> {
109 self.find_header("ACCEPT-LANGUAGE")
110 }
111
112 pub fn has_accept_charset_header(&self) -> bool {
114 self.has_header("ACCEPT-CHARSET")
115 }
116
117 pub fn accept_charset(&self) -> Vec<HeaderValue> {
119 self.find_header("ACCEPT-CHARSET")
120 }
121
122 pub fn has_accept_encoding_header(&self) -> bool {
124 self.has_header("ACCEPT-ENCODING")
125 }
126
127 pub fn accept_encoding(&self) -> Vec<HeaderValue> {
129 self.find_header("ACCEPT-ENCODING")
130 }
131
132 pub fn has_header(&self, header: &str) -> bool {
134 self.headers.keys().find(|k| k.to_uppercase() == header.to_uppercase()).is_some()
135 }
136
137 pub fn find_header(&self, header: &str) -> Vec<HeaderValue> {
140 match self.headers.keys().find(|k| k.to_uppercase() == header.to_uppercase()) {
141 Some(header) => self.headers.get(header).unwrap().clone(),
142 None => Vec::new()
143 }
144 }
145
146 pub fn has_header_value(&self, header: &str, value: &str) -> bool {
148 match self.headers.keys().find(|k| k.to_uppercase() == header.to_uppercase()) {
149 Some(header) => match self.headers.get(header).unwrap().iter().find(|val| *val == value) {
150 Some(_) => true,
151 None => false
152 },
153 None => false
154 }
155 }
156}
157
158#[derive(Debug, Clone, PartialEq)]
160pub struct WebmachineResponse {
161 pub status: u16,
163 pub headers: BTreeMap<String, Vec<HeaderValue>>,
165 pub body: Option<Bytes>
167}
168
169impl WebmachineResponse {
170 pub fn default() -> WebmachineResponse {
172 WebmachineResponse {
173 status: 200,
174 headers: BTreeMap::new(),
175 body: None
176 }
177 }
178
179 pub fn has_header(&self, header: &str) -> bool {
181 self.headers.keys().find(|k| k.to_uppercase() == header.to_uppercase()).is_some()
182 }
183
184 pub fn add_header(&mut self, header: &str, values: Vec<HeaderValue>) {
186 self.headers.insert(header.to_string(), values);
187 }
188
189 pub fn add_headers(&mut self, headers: HashMap<String, Vec<String>>) {
191 for (k, v) in headers {
192 self.headers.insert(k, v.iter().map(HeaderValue::basic).collect());
193 }
194 }
195
196 pub fn add_cors_headers(&mut self, allowed_methods: &Vec<String>) {
198 let cors_headers = WebmachineResponse::cors_headers(allowed_methods);
199 for (k, v) in cors_headers {
200 self.add_header(k.as_str(), v.iter().map(HeaderValue::basic).collect());
201 }
202 }
203
204 pub fn cors_headers(allowed_methods: &Vec<String>) -> HashMap<String, Vec<String>> {
206 hashmap!{
207 "Access-Control-Allow-Origin".to_string() => vec!["*".to_string()],
208 "Access-Control-Allow-Methods".to_string() => allowed_methods.clone(),
209 "Access-Control-Allow-Headers".to_string() => vec!["Content-Type".to_string()]
210 }
211 }
212
213 pub fn has_body(&self) -> bool {
215 match &self.body {
216 &None => false,
217 &Some(ref body) => !body.is_empty()
218 }
219 }
220}
221
222#[derive(Debug, Clone, PartialEq)]
224pub enum MetaDataValue {
225 Empty,
227 String(String),
229 UInteger(u64),
231 Integer(i64)
233}
234
235impl Display for MetaDataValue {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 match self {
238 MetaDataValue::String(s) => write!(f, "{}", s.as_str()),
239 MetaDataValue::UInteger(u) => write!(f, "{}", *u),
240 MetaDataValue::Integer(i) => write!(f, "{}", *i),
241 MetaDataValue::Empty => Ok(())
242 }
243 }
244}
245
246impl Default for MetaDataValue {
247 fn default() -> Self {
248 MetaDataValue::Empty
249 }
250}
251
252impl Default for &MetaDataValue {
253 fn default() -> Self {
254 &MetaDataValue::Empty
255 }
256}
257
258impl From<String> for MetaDataValue {
259 fn from(value: String) -> Self {
260 MetaDataValue::String(value)
261 }
262}
263
264impl From<&String> for MetaDataValue {
265 fn from(value: &String) -> Self {
266 MetaDataValue::String(value.clone())
267 }
268}
269
270impl From<&str> for MetaDataValue {
271 fn from(value: &str) -> Self {
272 MetaDataValue::String(value.to_string())
273 }
274}
275
276impl From<u16> for MetaDataValue {
277 fn from(value: u16) -> Self {
278 MetaDataValue::UInteger(value as u64)
279 }
280}
281
282impl From<i16> for MetaDataValue {
283 fn from(value: i16) -> Self {
284 MetaDataValue::Integer(value as i64)
285 }
286}
287
288impl From<u64> for MetaDataValue {
289 fn from(value: u64) -> Self {
290 MetaDataValue::UInteger(value)
291 }
292}
293
294impl From<i64> for MetaDataValue {
295 fn from(value: i64) -> Self {
296 MetaDataValue::Integer(value)
297 }
298}
299
300#[derive(Debug, Clone, PartialEq)]
302pub struct WebmachineContext {
303 pub request: WebmachineRequest,
305 pub response: WebmachineResponse,
307 pub selected_media_type: Option<String>,
309 pub selected_language: Option<String>,
311 pub selected_charset: Option<String>,
313 pub selected_encoding: Option<String>,
315 pub if_unmodified_since: Option<DateTime<FixedOffset>>,
317 pub if_modified_since: Option<DateTime<FixedOffset>>,
319 pub redirect: bool,
321 pub new_resource: bool,
323 pub metadata: HashMap<String, MetaDataValue>
325}
326
327impl Default for WebmachineContext {
328 fn default() -> WebmachineContext {
330 WebmachineContext {
331 request: WebmachineRequest::default(),
332 response: WebmachineResponse::default(),
333 selected_media_type: None,
334 selected_language: None,
335 selected_charset: None,
336 selected_encoding: None,
337 if_unmodified_since: None,
338 if_modified_since: None,
339 redirect: false,
340 new_resource: false,
341 metadata: HashMap::new()
342 }
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use expectest::prelude::*;
349
350 use crate::headers::*;
351
352 use super::*;
353
354 #[test]
355 fn request_does_not_have_header_test() {
356 let request = WebmachineRequest {
357 .. WebmachineRequest::default()
358 };
359 expect!(request.has_header("Vary")).to(be_false());
360 expect!(request.has_header_value("Vary", "*")).to(be_false());
361 }
362
363 #[test]
364 fn request_with_empty_header_test() {
365 let request = WebmachineRequest {
366 headers: hashmap!{ "HeaderA".to_string() => Vec::new() },
367 .. WebmachineRequest::default()
368 };
369 expect!(request.has_header("HeaderA")).to(be_true());
370 expect!(request.has_header_value("HeaderA", "*")).to(be_false());
371 }
372
373 #[test]
374 fn request_with_header_single_value_test() {
375 let request = WebmachineRequest {
376 headers: hashmap!{ "HeaderA".to_string() => vec![h!("*")] },
377 .. WebmachineRequest::default()
378 };
379 expect!(request.has_header("HeaderA")).to(be_true());
380 expect!(request.has_header_value("HeaderA", "*")).to(be_true());
381 expect!(request.has_header_value("HeaderA", "other")).to(be_false());
382 }
383
384 #[test]
385 fn request_with_header_multiple_value_test() {
386 let request = WebmachineRequest {
387 headers: hashmap!{ "HeaderA".to_string() => vec![h!("*"), h!("other")]},
388 .. WebmachineRequest::default()
389 };
390 expect!(request.has_header("HeaderA")).to(be_true());
391 expect!(request.has_header_value("HeaderA", "*")).to(be_true());
392 expect!(request.has_header_value("HeaderA", "other")).to(be_true());
393 expect!(request.has_header_value("HeaderA", "other2")).to(be_false());
394 }
395}