volga/http/response/
html.rs

1//! Macros for HTML responses
2
3/// Produces `OK 200` response with HTML body
4/// 
5/// # Examples
6/// ## Default usage
7///```no_run
8/// # use volga::HttpRequest;
9/// use volga::html;
10///
11/// # async fn dox(request: HttpRequest) -> std::io::Result<()> {
12/// html!(
13///    r#"
14///    <!doctype html>
15///    <html>
16///        <head>Hello!</head>
17///        <body>
18///            <p>Hello, World!</p>
19///        </body>
20///    </html>
21///    "#);
22/// # Ok(())
23/// # }
24#[macro_export]
25macro_rules! html {
26    ($body:expr) => {
27        $crate::html!($body, [])
28    };
29    ($body:expr, [ $( ($key:expr, $value:expr) ),* $(,)? ]) => {
30        $crate::response!(
31            $crate::http::StatusCode::OK, 
32            $crate::HttpBody::full($body),
33            [
34                ($crate::headers::CONTENT_TYPE, "text/html; charset=utf-8"),
35                $( ($key, $value) ),*
36            ]
37        )
38    };
39}
40
41/// Produces `OK 200` response with HTML file body
42/// 
43/// # Examples
44/// ## Default usage
45///```no_run
46/// # use volga::HttpRequest;
47/// use volga::html_file;
48/// use tokio::fs::File;
49///
50/// # async fn dox(request: HttpRequest) -> std::io::Result<()> {
51/// let index_name = "index.html";
52/// let index_file = File::open(index_name).await?;
53/// html_file!(index_name, index_file);
54/// # Ok(())
55/// # }
56#[macro_export]
57macro_rules! html_file {
58    ($file_name:expr, $body:expr) => {
59        $crate::html_file!($file_name, $body, [])
60    };
61    ($file_name:expr, $body:expr, [ $( ($key:expr, $value:expr) ),* $(,)? ]) => {{
62        let mime = $crate::fs::get_mime_or_octet_stream($file_name);
63        $crate::response!(
64            $crate::http::StatusCode::OK, 
65            $crate::HttpBody::file($body),
66            [
67                ($crate::headers::CONTENT_TYPE, mime.as_ref()),
68                ($crate::headers::TRANSFER_ENCODING, "chunked"),
69                $( ($key, $value) ),*
70            ]
71        )
72    }};
73}
74
75/// Produces `NO CONTENT 204` response
76/// 
77/// # Examples
78/// ## Default usage
79///```no_run
80/// # use volga::HttpRequest;
81/// use volga::no_content;
82///
83/// # async fn dox(request: HttpRequest) -> std::io::Result<()> {
84/// no_content!();
85/// # Ok(())
86/// # } 
87#[macro_export]
88macro_rules! no_content {
89    () => {
90        $crate::response!(
91            $crate::http::StatusCode::NO_CONTENT,
92            $crate::HttpBody::empty()
93        )
94    };
95}
96
97#[cfg(test)]
98mod tests {
99    use http_body_util::BodyExt;
100    use crate::test_utils::read_file_bytes;
101
102    #[tokio::test]
103    async fn it_creates_html_response() {
104        let html_text = 
105            r#"
106            <!doctype html>
107            <html>
108                <head>Hello!</head>
109                <body>
110                    <p>Hello, World!</p>
111                </body>
112            </html>
113            "#;
114        
115        let response = html!(html_text);
116
117        assert!(response.is_ok());
118
119        let mut response = response.unwrap();
120        let body = read_file_bytes(&mut response).await;
121
122        assert_eq!(String::from_utf8_lossy(body.as_slice()), html_text);
123        assert_eq!(response.status(), 200);
124    }
125
126    #[tokio::test]
127    async fn it_creates_html_response_with_headers() {
128        let html_text =
129            r#"
130            <!doctype html>
131            <html>
132                <head>Hello!</head>
133                <body>
134                    <p>Hello, World!</p>
135                </body>
136            </html>
137            "#;
138
139        let response = html!(html_text, [
140            ("x-api-key", "some api key")
141        ]);
142
143        assert!(response.is_ok());
144
145        let mut response = response.unwrap();
146        let body = read_file_bytes(&mut response).await;
147
148        assert_eq!(String::from_utf8_lossy(body.as_slice()), html_text);
149        assert_eq!(response.headers()["x-api-key"], "some api key");
150        assert_eq!(response.status(), 200);
151    }
152
153    #[tokio::test]
154    async fn it_creates_no_content_response() {
155        let response = no_content!();
156
157        assert!(response.is_ok());
158
159        let mut response = response.unwrap();
160        let body = response.body_mut().collect().await.unwrap().to_bytes();
161
162        assert_eq!(body.len(), 0);
163        assert_eq!(response.status(), 204);
164    }
165}