pillow_http/response/
mod.rs

1use crate::header::{self, ContentType};
2pub mod static_files;
3pub mod status_code;
4
5/// Response struct to client
6#[derive(Debug, PartialEq, Eq, Clone)]
7pub struct Response {
8    /// Like 200 OK
9    status_code: StatusCode,
10
11    /// Response Headers
12    headers: HashMap<Header, String>,
13
14    // template_engine: Template,
15    /// Cross Origin Site
16    pub cors: String,
17
18    /// Content of Response
19    /// Like html, json, other.
20    content: Body,
21}
22
23#[derive(Debug, PartialEq, Eq, Clone)]
24pub enum Body {
25    STRING(String),
26    BYTES(Vec<u8>),
27}
28
29impl std::fmt::Display for Body {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        match self {
32            Body::STRING(string) => write!(f, "{}", string),
33            Body::BYTES(bytes) => {
34                let bytes_str = bytes
35                    .iter()
36                    .map(|&byte| format!("{:02X}", byte))
37                    .collect::<String>();
38                write!(f, "{}", bytes_str)
39            }
40        }
41    }
42}
43
44impl Body {
45    pub fn as_bytes(&self) -> &[u8] {
46        match self {
47            Body::STRING(s) => s.as_bytes(),
48
49            Body::BYTES(b) => b.as_slice(),
50        }
51    }
52}
53
54use std::collections::HashMap;
55
56use chrono;
57use serde_json::Value;
58
59use pillow_templates::Template;
60
61use self::{
62    header::Header,
63    status_code::{AsStr, StatusCode},
64};
65
66impl Response {
67    /// Returns a new Response
68    pub fn new_empty() -> Response {
69        Response {
70            status_code: StatusCode::Successfull(status_code::Successfull::OK),
71
72            headers: HashMap::from([
73                (Header::Server, String::from("Pillow")),
74                // (Header::ETag, String::from(r#""3314042""#)),
75            ]),
76
77            cors: String::from("*"),
78
79            content: Body::STRING(String::new()),
80        }
81    }
82}
83
84impl Response {
85    /// Send a html file from resources/views directory
86    ///
87    /// # Arguments
88    ///
89    /// * page - A String that the page on views directory
90    ///
91    /// # Examples
92    ///
93    /// ```rust
94    /// use pillow::http::*;
95    ///
96    /// #[controller(method = "GET", path = "/")]
97    /// fn index() -> Response {
98    ///     Response::html("index")
99    /// }
100    /// ```
101    pub fn html(page: &'static str) -> Response {
102        let mut response = Self::new_empty();
103
104        let html = Template::Html(page);
105        let contents = html.render();
106
107        let date = crate::get_date_now!();
108
109        response.add_multiple_headers(vec![
110            (Header::AccessControlAllowOrigin, response.cors.to_string()),
111            (Header::Connection, "Keep-Alive".to_string()),
112            (Header::ContentLength, contents.len().to_string()),
113            (Header::ContentType, "text/html; charset=utf-8".to_string()),
114            (Header::Date, date.to_string()),
115            (Header::LastModified, date.to_string()),
116        ]);
117
118        response.insert_string_content(contents);
119
120        response
121    }
122
123    /// Send a html file from resources/views directory
124    ///
125    /// # Arguments
126    ///
127    /// * Template - Enum contain the name of file in resources/views
128    ///
129    /// # Examples
130    ///
131    /// ```rust
132    /// use pillow::{
133    ///     http::*,
134    ///     templates::{Context, Template}
135    /// };
136    ///
137    /// #[controller(method = "GET", path = "/")]
138    ///  fn index() -> Response {
139    ///     let mut ctx = Context::new();
140    ///
141    ///     Response::view(Template::Tera("index", "tera.html", ctx))
142    /// }
143    /// ```
144    pub fn view(template: Template) -> Response {
145        let mut response = Self::new_empty();
146
147        let contents = template.render();
148
149        let date = crate::get_date_now!();
150
151        response.add_multiple_headers(vec![
152            (Header::AccessControlAllowOrigin, response.cors.to_string()),
153            (Header::Connection, "Keep-Alive".to_string()),
154            (Header::ContentLength, contents.len().to_string()),
155            (Header::ContentType, "text/html; charset=utf-8".to_string()),
156            (Header::Date, date.to_string()),
157            (Header::LastModified, date.to_string()),
158        ]);
159
160        response.insert_string_content(contents);
161
162        response
163    }
164
165    /// Send a hbs file from resources/views directory
166    ///
167    /// # Arguments
168    ///
169    /// * page - A String that the page on views directory
170    /// * data - Json value
171    ///
172    /// # Examples
173    ///
174    /// ```rust
175    /// use pillow::json;
176    /// use pillow::http::*;
177    ///
178    /// #[controller(method = "GET", path = "/")]
179    ///  fn index() -> Response {
180    ///     let json = json! ({
181    ///         "name": "foo"
182    ///     });
183    ///
184    ///     Response::hbs("index", json)
185    /// }
186    /// ```
187    pub fn hbs(page: &'static str, data: Value) -> Response {
188        let mut response = Self::new_empty();
189
190        let hbs = Template::Handlebars(page, data);
191        let contents = hbs.render();
192
193        let date = crate::get_date_now!();
194
195        response.add_multiple_headers(vec![
196            (Header::AccessControlAllowOrigin, response.cors.to_string()),
197            (Header::Connection, "Keep-Alive".to_string()),
198            (Header::ContentLength, contents.len().to_string()),
199            (Header::ContentType, "text/html; charset=utf-8".to_string()),
200            (Header::Date, date.to_string()),
201            (Header::LastModified, date.to_string()),
202        ]);
203
204        response.insert_string_content(contents);
205
206        response
207    }
208
209    /// Send a json from macro json!
210    ///
211    /// # Arguments
212    ///
213    /// * json - A string slice that sends to http client
214    ///
215    /// # Examples
216    ///
217    /// ```rust
218    /// use pillow::json;
219    /// use pillow::http::{ MainRouter, Response };
220    ///
221    /// #[controller(method = "GET", path = "/")]
222    ///  fn index() -> Response {
223    ///     let json = json! ({
224    ///         "name": "foo"
225    ///     });
226    ///
227    ///     Response::json(json)
228    /// }
229    /// ```
230    pub fn json(js: Value) -> Response {
231        let mut response = Self::new_empty();
232
233        let date = crate::get_date_now!();
234        let json = js.to_string();
235
236        response.add_multiple_headers(vec![
237            (Header::AccessControlAllowOrigin, response.cors.to_string()),
238            (Header::AcceptRanges, "bytes".to_string()),
239            (Header::ContentLength, json.len().to_string()),
240            (
241                Header::ContentType,
242                "application/json; charset=utf-8".to_string(),
243            ),
244            (Header::Date, date.to_string()),
245            (Header::Date, date.to_string()),
246            (Header::Vary, "Accept-Encoding".to_string()),
247        ]);
248
249        response.insert_string_content(json);
250
251        response
252    }
253
254    /// Send a json from str
255    ///
256    /// # Arguments
257    ///
258    /// * json - A string slice that sends to http client
259    ///
260    /// # Examples
261    ///
262    /// ```rust
263    /// use pillow::http::*;
264    ///
265    /// #[controller(method = "GET", path = "/")]
266    ///  fn index() -> Response {
267    ///     Response::json_from_str(r#{
268    ///         "name": "foo"
269    ///     }#)
270    /// }
271    /// ```
272    pub fn json_from_str(json: &str) -> Response {
273        let mut response = Self::new_empty();
274
275        let date = crate::get_date_now!();
276
277        let json_value: Value = serde_json::from_str(json).unwrap();
278        let js = json_value.to_string();
279
280        response.add_multiple_headers(vec![
281            (Header::AccessControlAllowOrigin, response.cors.to_string()),
282            (Header::AcceptRanges, "bytes".to_string()),
283            (Header::ContentLength, js.len().to_string()),
284            (
285                Header::ContentType,
286                "application/json; charset=utf-8".to_string(),
287            ),
288            (Header::Date, date.to_string()),
289            (Header::Date, date.to_string()),
290            (Header::Vary, "Accept-Encoding".to_string()),
291        ]);
292
293        // let response = format!("{status_line}{res}\r\n\r\n{js}");
294        response.insert_string_content(js);
295
296        response
297    }
298
299    /// Send text to client
300    ///
301    /// # Examples
302    ///
303    /// ```rust
304    /// use pillow::http::*;
305    ///
306    /// #[controller(method = "GET", path = "/")]
307    ///  fn index() -> Response {
308    ///     Response::text("hello world")
309    /// }
310    /// ```
311    pub fn text(txt: &str) -> Response {
312        let mut response = Self::new_empty();
313
314        let length = txt.len();
315
316        response.add_multiple_headers(vec![
317            (Header::AccessControlAllowOrigin, response.cors.to_string()),
318            (Header::ContentType, "text/plain".to_string()),
319            (Header::ContentLength, length.to_string()),
320        ]);
321
322        response.insert_string_content(txt.to_string());
323
324        response
325    }
326
327    /// Send css response to client
328    pub fn css(&mut self, css: String) -> String {
329        let status_line = self.get_status_line();
330
331        let date = chrono::offset::Local::now();
332
333        self.add_multiple_headers(vec![
334            (Header::AccessControlAllowOrigin, self.cors.to_string()),
335            (Header::ContentLength, css.len().to_string()),
336            // ("Content-Encoding", "br".to_string());
337            (Header::ContentType, "text/css; charset=utf-8".to_string()),
338            (Header::Date, date.to_string()),
339            (Header::LastModified, date.to_string()),
340            // ("Transfer-Encoding", "chunked".to_string()),
341            // ("Vary", "Accept-Encoding".to_string()),
342        ]);
343
344        let headers = self.get_headers();
345        let response = format!("{status_line}{headers}\r\n\r\n{css}");
346
347        response
348    }
349
350    /// Send javascript to client
351    pub fn javascript(&mut self, js: String) -> String {
352        let status_line = self.get_status_line();
353
354        let date = chrono::offset::Local::now();
355
356        self.add_multiple_headers(vec![
357            (Header::AccessControlAllowOrigin, self.cors.to_string()),
358            (Header::ContentLength, js.len().to_string()),
359            // ("Content-Encoding", "gzip".to_string()),
360            (
361                Header::ContentType,
362                "application/javascript; charset=utf-8".to_string(),
363            ),
364            (Header::Date, date.to_string()),
365            (Header::LastModified, date.to_string()),
366            // ("Transfer-Encoding", String::from("chunked")),
367            // ("Vary", "Accept-Encoding".to_string())
368        ]);
369
370        let headers = self.get_headers();
371        let response = format!("{status_line}{headers}\r\n\r\n{js}");
372
373        response
374    }
375
376    pub fn file(content_type: ContentType, content: Vec<u8>) -> Response {
377        let mut response = Self::new_empty();
378
379        let date = crate::get_date_now!();
380
381        response.add_multiple_headers(vec![
382            (Header::AccessControlAllowOrigin, response.cors.to_string()),
383            (Header::Connection, "Keep-Alive".to_string()),
384            // (Header::ContentDisposition, "inline".to_string()),
385            (Header::ContentLength, content.len().to_string()),
386            (Header::Date, date.to_string()),
387        ]);
388
389        response.content_type(content_type);
390
391        response.insert_bytes_content(content.clone());
392
393        response
394    }
395}
396
397impl Response {
398    pub fn redirect(location: &'static str) -> Response {
399        let mut response = Self::new_empty();
400
401        response.set_status_code(StatusCode::Redirection(status_code::Redirection::Found));
402
403        response.add_multiple_headers(vec![
404            (Header::AccessControlAllowOrigin, response.cors.to_string()),
405            (Header::Location, location.to_string()),
406        ]);
407
408        response
409    }
410}
411
412impl ToString for Response {
413    /// Convert Response struct in String
414    fn to_string(&self) -> String {
415        let res = format!(
416            "{}{}\r\n\r\n{}",
417            &self.get_status_line(),
418            &self.get_headers(),
419            &self.content
420        );
421
422        res
423    }
424}
425
426impl Response {
427    /// Insert content in the Response.body
428    pub fn insert_string_content(&mut self, content: String) {
429        self.content = Body::STRING(content);
430    }
431
432    pub fn insert_bytes_content(&mut self, content: Vec<u8>) {
433        self.content = Body::BYTES(content);
434    }
435}
436
437impl Response {
438    /// Change Content-Type of the response
439    pub fn content_type(&mut self, content_type: header::ContentType) -> &Response {
440        self.add_header(Header::ContentType, content_type.as_str().to_string());
441        self
442    }
443}
444
445impl Response {
446    pub fn websocket_upgrade_connection() -> Response {
447        let mut response = Response::new_empty();
448
449        response.set_status_code(StatusCode::Information(
450            status_code::Information::SwitchingProtocols,
451        ));
452
453        response.clear_headers();
454
455        response.add_multiple_headers(vec![
456            (Header::Upgrade, "websocket".to_string()),
457            (Header::Connection, "Upgrade".to_string()),
458            (
459                Header::SecWebSocketAccept,
460                "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=".to_string(),
461            ),
462            (Header::SecWebSocketProtocol, "superchat".to_string()),
463        ]);
464
465        println!("Response: {:#?}", &response.to_string());
466
467        response
468    }
469}
470
471impl Response {
472    /// Add header to response
473    ///
474    /// # Arguments
475    ///
476    /// * `header` - Header name
477    /// * `value` - Header value
478    ///
479    /// # Examples
480    ///
481    /// ```rust
482    /// use pillow::http::Router;
483    ///
484    /// let mut app = Router::new();
485    ///
486    /// app.get("/", |_, response| {
487    ///     response.add_header(Header::ContentType, "text/hmtl".to_string());
488    ///     response.view("index")
489    /// })
490    /// ```
491    pub fn add_header(&mut self, header: Header, value: String) {
492        self.headers.insert(header, value);
493    }
494
495    /// Add multiple headers to response
496    ///
497    /// # Examples
498    ///
499    /// ```rust
500    /// use pillow::http::*;
501    ///
502    /// #[controller(method="GET", path = "/")]
503    /// fn index() {
504    ///     let response: Response = Response::new_empty();
505    ///
506    ///     response.add_multiple_headers(vec![
507    ///         (Header::ContentType, "text/html".to_string()),
508    ///         (Header::AccessControlAllowOrigin, "*".to_string())
509    ///     ])
510    ///
511    ///     response
512    /// }
513    /// ```
514    pub fn add_multiple_headers(&mut self, headers: Vec<(Header, String)>) {
515        for (header, value) in headers {
516            self.add_header(header, value);
517        }
518    }
519
520    /// Get All headers in one string
521    ///
522    /// # Examples
523    ///
524    /// ```rust
525    /// let headers: String = self.get_headers();
526    /// ```
527    pub fn get_headers(&self) -> String {
528        let mut res = String::new();
529
530        for (header, value) in &self.headers {
531            let header = &header.as_str();
532            res = format!("{res}\r\n{header}: {value}");
533        }
534
535        res
536    }
537
538    /// Clear All headers
539    pub fn clear_headers(&mut self) {
540        self.headers = HashMap::new();
541    }
542
543    /// Get Status Line
544    /// HTTP/1.1 200 OK
545    ///
546    /// # Examples
547    ///
548    /// ```rust
549    /// let status_line: String = self.get_status_line();
550    ///
551    /// assert_eq!(status_line, "HTTP/1.1 200 OK".to_string());
552    /// ```
553    pub fn get_status_line(&self) -> String {
554        let status_code = &self.status_code;
555        let status_line = format!("HTTP/1.1 {}", status_code.as_str());
556
557        status_line
558    }
559
560    /// Set Status Code Like 200 OK
561    ///
562    /// # Examples
563    ///
564    /// ```rust
565    /// let response: Response = Response::new_empty();
566    /// response.set_status_code(StatusCode::Successfull(status_code::Successfull::OK));
567    /// ```
568    pub fn set_status_code(&mut self, code: StatusCode) {
569        self.status_code = code;
570    }
571
572    pub fn get_body(&self) -> Body {
573        self.content.clone()
574    }
575}
576
577#[macro_export]
578macro_rules! get_date_now {
579    () => {{
580        let date = chrono::offset::Local::now();
581
582        date
583    }};
584}