1use std::any::Any;
5use std::collections::{BTreeMap, HashMap};
6use std::fmt::{Debug, Display};
7use std::sync::Arc;
8use std::time::SystemTime;
9use bytes::Bytes;
10use chrono::{DateTime, FixedOffset};
11use maplit::hashmap;
12
13use crate::headers::HeaderValue;
14
15#[derive(Debug, Clone, PartialEq)]
17pub struct WebmachineRequest {
18 pub request_path: String,
20 pub base_path: String,
22 pub sub_path: Option<String>,
24 pub path_vars: HashMap<String, String>,
26 pub method: String,
28 pub headers: HashMap<String, Vec<HeaderValue>>,
30 pub body: Option<Bytes>,
32 pub query: HashMap<String, Vec<String>>
34}
35
36impl Default for WebmachineRequest {
37 fn default() -> WebmachineRequest {
39 WebmachineRequest {
40 request_path: "/".to_string(),
41 base_path: "/".to_string(),
42 sub_path: None,
43 path_vars: Default::default(),
44 method: "GET".to_string(),
45 headers: HashMap::new(),
46 body: None,
47 query: HashMap::new()
48 }
49 }
50}
51
52impl WebmachineRequest {
53 pub fn content_type(&self) -> HeaderValue {
56 match self.headers.keys().find(|k| k.to_uppercase() == "CONTENT-TYPE") {
57 Some(header) => match self.headers.get(header).unwrap().first() {
58 Some(value) => value.clone(),
59 None => HeaderValue::json()
60 },
61 None => HeaderValue::json()
62 }
63 }
64
65 pub fn is_put_or_post(&self) -> bool {
67 ["PUT", "POST"].contains(&self.method.to_uppercase().as_str())
68 }
69
70 pub fn is_get_or_head(&self) -> bool {
72 ["GET", "HEAD"].contains(&self.method.to_uppercase().as_str())
73 }
74
75 pub fn is_get(&self) -> bool {
77 self.method.to_uppercase() == "GET"
78 }
79
80 pub fn is_options(&self) -> bool {
82 self.method.to_uppercase() == "OPTIONS"
83 }
84
85 pub fn is_put(&self) -> bool {
87 self.method.to_uppercase() == "PUT"
88 }
89
90 pub fn is_post(&self) -> bool {
92 self.method.to_uppercase() == "POST"
93 }
94
95 pub fn is_delete(&self) -> bool {
97 self.method.to_uppercase() == "DELETE"
98 }
99
100 pub fn has_accept_header(&self) -> bool {
102 self.has_header("ACCEPT")
103 }
104
105 pub fn accept(&self) -> Vec<HeaderValue> {
107 self.find_header("ACCEPT")
108 }
109
110 pub fn has_accept_language_header(&self) -> bool {
112 self.has_header("ACCEPT-LANGUAGE")
113 }
114
115 pub fn accept_language(&self) -> Vec<HeaderValue> {
117 self.find_header("ACCEPT-LANGUAGE")
118 }
119
120 pub fn has_accept_charset_header(&self) -> bool {
122 self.has_header("ACCEPT-CHARSET")
123 }
124
125 pub fn accept_charset(&self) -> Vec<HeaderValue> {
127 self.find_header("ACCEPT-CHARSET")
128 }
129
130 pub fn has_accept_encoding_header(&self) -> bool {
132 self.has_header("ACCEPT-ENCODING")
133 }
134
135 pub fn accept_encoding(&self) -> Vec<HeaderValue> {
137 self.find_header("ACCEPT-ENCODING")
138 }
139
140 pub fn has_header(&self, header: &str) -> bool {
142 self.headers.keys().find(|k| k.to_uppercase() == header.to_uppercase()).is_some()
143 }
144
145 pub fn find_header(&self, header: &str) -> Vec<HeaderValue> {
148 match self.headers.keys().find(|k| k.to_uppercase() == header.to_uppercase()) {
149 Some(header) => self.headers.get(header).unwrap().clone(),
150 None => Vec::new()
151 }
152 }
153
154 pub fn has_header_value(&self, header: &str, value: &str) -> bool {
156 match self.headers.keys().find(|k| k.to_uppercase() == header.to_uppercase()) {
157 Some(header) => match self.headers.get(header).unwrap().iter().find(|val| *val == value) {
158 Some(_) => true,
159 None => false
160 },
161 None => false
162 }
163 }
164}
165
166#[derive(Debug, Clone, PartialEq)]
168pub struct WebmachineResponse {
169 pub status: u16,
171 pub headers: BTreeMap<String, Vec<HeaderValue>>,
173 pub body: Option<Bytes>
175}
176
177impl WebmachineResponse {
178 pub fn default() -> WebmachineResponse {
180 WebmachineResponse {
181 status: 200,
182 headers: BTreeMap::new(),
183 body: None
184 }
185 }
186
187 pub fn has_header(&self, header: &str) -> bool {
189 self.headers.keys().find(|k| k.to_uppercase() == header.to_uppercase()).is_some()
190 }
191
192 pub fn add_header(&mut self, header: &str, values: Vec<HeaderValue>) {
194 self.headers.insert(header.to_string(), values);
195 }
196
197 pub fn add_headers(&mut self, headers: HashMap<String, Vec<String>>) {
199 for (k, v) in headers {
200 self.headers.insert(k, v.iter().map(HeaderValue::basic).collect());
201 }
202 }
203
204 pub fn add_cors_headers(&mut self, allowed_methods: &[&str]) {
206 let cors_headers = WebmachineResponse::cors_headers(allowed_methods);
207 for (k, v) in cors_headers {
208 self.add_header(k.as_str(), v.iter().map(HeaderValue::basic).collect());
209 }
210 }
211
212 pub fn cors_headers(allowed_methods: &[&str]) -> HashMap<String, Vec<String>> {
214 hashmap!{
215 "Access-Control-Allow-Origin".to_string() => vec!["*".to_string()],
216 "Access-Control-Allow-Methods".to_string() => allowed_methods.iter().map(|v| v.to_string()).collect(),
217 "Access-Control-Allow-Headers".to_string() => vec!["Content-Type".to_string()]
218 }
219 }
220
221 pub fn has_body(&self) -> bool {
223 match &self.body {
224 &None => false,
225 &Some(ref body) => !body.is_empty()
226 }
227 }
228}
229
230pub trait MetaDataThing: Any + Debug {}
232
233#[derive(Debug, Clone)]
235pub enum MetaDataValue {
236 Empty,
238 String(String),
240 UInteger(u64),
242 Integer(i64),
244 Anything(Arc<dyn MetaDataThing + Send + Sync>)
246}
247
248impl MetaDataValue {
249 pub fn is_empty(&self) -> bool {
251 match self {
252 MetaDataValue::Empty => true,
253 _ => false
254 }
255 }
256
257 pub fn as_string(&self) -> Option<String> {
259 match self {
260 MetaDataValue::String(s) => Some(s.clone()),
261 _ => None
262 }
263 }
264
265 pub fn as_uint(&self) -> Option<u64> {
267 match self {
268 MetaDataValue::UInteger(u) => Some(*u),
269 _ => None
270 }
271 }
272
273 pub fn as_int(&self) -> Option<i64> {
275 match self {
276 MetaDataValue::Integer(i) => Some(*i),
277 _ => None
278 }
279 }
280
281 pub fn as_anything(&self) -> Option<&(dyn Any + Send + Sync)> {
283 match self {
284 MetaDataValue::Anything(thing) => Some(thing.as_ref()),
285 _ => None
286 }
287 }
288}
289
290impl Display for MetaDataValue {
291 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
292 match self {
293 MetaDataValue::String(s) => write!(f, "{}", s.as_str()),
294 MetaDataValue::UInteger(u) => write!(f, "{}", *u),
295 MetaDataValue::Integer(i) => write!(f, "{}", *i),
296 MetaDataValue::Empty => Ok(()),
297 MetaDataValue::Anything(thing) => write!(f, "any({:?})", thing)
298 }
299 }
300}
301
302impl Default for MetaDataValue {
303 fn default() -> Self {
304 MetaDataValue::Empty
305 }
306}
307
308impl Default for &MetaDataValue {
309 fn default() -> Self {
310 &MetaDataValue::Empty
311 }
312}
313
314impl From<String> for MetaDataValue {
315 fn from(value: String) -> Self {
316 MetaDataValue::String(value)
317 }
318}
319
320impl From<&String> for MetaDataValue {
321 fn from(value: &String) -> Self {
322 MetaDataValue::String(value.clone())
323 }
324}
325
326impl From<&str> for MetaDataValue {
327 fn from(value: &str) -> Self {
328 MetaDataValue::String(value.to_string())
329 }
330}
331
332impl From<u16> for MetaDataValue {
333 fn from(value: u16) -> Self {
334 MetaDataValue::UInteger(value as u64)
335 }
336}
337
338impl From<i16> for MetaDataValue {
339 fn from(value: i16) -> Self {
340 MetaDataValue::Integer(value as i64)
341 }
342}
343
344impl From<u64> for MetaDataValue {
345 fn from(value: u64) -> Self {
346 MetaDataValue::UInteger(value)
347 }
348}
349
350impl From<i64> for MetaDataValue {
351 fn from(value: i64) -> Self {
352 MetaDataValue::Integer(value)
353 }
354}
355
356#[derive(Debug, Clone)]
358pub struct WebmachineContext {
359 pub request: WebmachineRequest,
361 pub response: WebmachineResponse,
363 pub selected_media_type: Option<String>,
365 pub selected_language: Option<String>,
367 pub selected_charset: Option<String>,
369 pub selected_encoding: Option<String>,
371 pub if_unmodified_since: Option<DateTime<FixedOffset>>,
373 pub if_modified_since: Option<DateTime<FixedOffset>>,
375 pub redirect: bool,
377 pub new_resource: bool,
379 pub metadata: HashMap<String, MetaDataValue>,
381 pub start_time: SystemTime
383}
384
385impl WebmachineContext {
386 pub fn downcast_metadata_value<'a, T: 'static>(&'a self, key: &'a str) -> Option<&'a T> {
388 self.metadata.get(key)
389 .and_then(|value| value.as_anything())
390 .and_then(|value| value.downcast_ref())
391 }
392}
393
394impl Default for WebmachineContext {
395 fn default() -> WebmachineContext {
397 WebmachineContext {
398 request: WebmachineRequest::default(),
399 response: WebmachineResponse::default(),
400 selected_media_type: None,
401 selected_language: None,
402 selected_charset: None,
403 selected_encoding: None,
404 if_unmodified_since: None,
405 if_modified_since: None,
406 redirect: false,
407 new_resource: false,
408 metadata: HashMap::new(),
409 start_time: SystemTime::now()
410 }
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use expectest::prelude::*;
417
418 use crate::headers::*;
419
420 use super::*;
421
422 #[test]
423 fn request_does_not_have_header_test() {
424 let request = WebmachineRequest {
425 .. WebmachineRequest::default()
426 };
427 expect!(request.has_header("Vary")).to(be_false());
428 expect!(request.has_header_value("Vary", "*")).to(be_false());
429 }
430
431 #[test]
432 fn request_with_empty_header_test() {
433 let request = WebmachineRequest {
434 headers: hashmap!{ "HeaderA".to_string() => Vec::new() },
435 .. WebmachineRequest::default()
436 };
437 expect!(request.has_header("HeaderA")).to(be_true());
438 expect!(request.has_header_value("HeaderA", "*")).to(be_false());
439 }
440
441 #[test]
442 fn request_with_header_single_value_test() {
443 let request = WebmachineRequest {
444 headers: hashmap!{ "HeaderA".to_string() => vec![h!("*")] },
445 .. WebmachineRequest::default()
446 };
447 expect!(request.has_header("HeaderA")).to(be_true());
448 expect!(request.has_header_value("HeaderA", "*")).to(be_true());
449 expect!(request.has_header_value("HeaderA", "other")).to(be_false());
450 }
451
452 #[test]
453 fn request_with_header_multiple_value_test() {
454 let request = WebmachineRequest {
455 headers: hashmap!{ "HeaderA".to_string() => vec![h!("*"), h!("other")]},
456 .. WebmachineRequest::default()
457 };
458 expect!(request.has_header("HeaderA")).to(be_true());
459 expect!(request.has_header_value("HeaderA", "*")).to(be_true());
460 expect!(request.has_header_value("HeaderA", "other")).to(be_true());
461 expect!(request.has_header_value("HeaderA", "other2")).to(be_false());
462 }
463}