rouille/input/accept.rs
1// Copyright (c) 2016 The Rouille developers
2// Licensed under the Apache License, Version 2.0
3// <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6// at your option. All files in the project carrying such
7// notice may not be copied, modified, or distributed except
8// according to those terms.
9
10/// Dispatches between blocks depending on the value of the `Accept` header.
11///
12/// This macro takes as first parameter the request object, and then each additional parameter must
13/// be of the form `mime => value` where `mime` is a MIME type in quotes and `value` is an
14/// expression of any type.
15///
16/// The macro returns the value corresponding to the MIME type that has the highest priority in
17/// the request's `Accept` header. If multiple MIME types have the same priority, the earliest in
18/// the list passed to the macro is chosen. If no MIME matches the request, the first in the list
19/// is chosen. If there is no `Accept` header in the request, it is as if the header's value
20/// was `*/*`.
21///
22/// You can also use `*` in the MIME types you pass to the macro. The MIME `*/*` can be used as a
23/// default handler.
24///
25/// > **Note**: Using `|` just like in real match expressions is not yet supported because the
26/// > author didn't find a way to make it work with Rust macros.
27///
28/// # Basic example
29///
30/// ```
31/// # #[macro_use] extern crate rouille;
32/// use rouille::Request;
33/// use rouille::Response;
34///
35/// fn handle(request: &Request) -> Response {
36/// accept!(request,
37/// "text/html" => Response::html("<p>Hello world</p>"),
38/// "text/plain" => Response::text("Hello world"),
39/// )
40/// }
41/// # fn main() {}
42/// ```
43///
44/// # Example with a default handler
45///
46/// ```
47/// # #[macro_use] extern crate rouille;
48/// use rouille::Request;
49/// use rouille::Response;
50///
51/// fn handle(request: &Request) -> Response {
52/// accept!(request,
53/// "text/html" => Response::html("<p>Hello world</p>"),
54/// "text/plain" => Response::text("Hello world"),
55/// "*/*" => Response::empty_406()
56/// )
57/// }
58/// # fn main() {}
59/// ```
60#[macro_export]
61macro_rules! accept {
62 ($request:expr, $($mime:expr => $val:expr),+ $(,)*) => ({
63 use $crate::input;
64 use std::iter;
65
66 let header = $request.header("Accept").unwrap_or("*/*");
67
68 let handled = {
69 let i = iter::empty();
70 $(let i = i.chain(iter::once($mime));)+
71 i
72 };
73
74 let mut preferred = input::priority_header_preferred(header, handled).unwrap_or(0);
75
76 let mut outcome = None;
77
78 preferred += 1;
79 $(
80 if preferred >= 1 {
81 preferred -= 1;
82 if preferred == 0 {
83 outcome = Some($val);
84 }
85 }
86 )+
87
88 outcome.unwrap() // unwrap() can only panic if priority_header_preferred has a bug or
89 // if the list of mimes is empty (which can't happen)
90 });
91}
92
93#[cfg(test)]
94mod tests {
95 use Request;
96
97 #[test]
98 fn basic() {
99 let request = Request::fake_http(
100 "GET",
101 "/",
102 vec![("Accept".to_owned(), "text/plain, */*".to_owned())],
103 vec![],
104 );
105
106 let result = accept!(&request,
107 "text/plain" => 5,
108 "*/*" => 12,
109 );
110
111 assert_eq!(result, 5);
112 }
113
114 #[test]
115 fn wildcard() {
116 let request = Request::fake_http(
117 "GET",
118 "/",
119 vec![("Accept".to_owned(), "image/gif".to_owned())],
120 vec![],
121 );
122
123 let result = accept!(&request,
124 "text/plain" => 5,
125 "*/*" => 12,
126 );
127
128 assert_eq!(result, 12);
129 }
130
131 #[test]
132 fn no_match() {
133 let request = Request::fake_http(
134 "GET",
135 "/",
136 vec![("Accept".to_owned(), "image/gif".to_owned())],
137 vec![],
138 );
139
140 let result = accept!(&request,
141 "text/plain" => 5,
142 "image/svg+xml" => 12,
143 );
144
145 assert_eq!(result, 5);
146 }
147
148 #[test]
149 fn multimatch_first() {
150 let request = Request::fake_http(
151 "GET",
152 "/",
153 vec![("Accept".to_owned(), "image/gif".to_owned())],
154 vec![],
155 );
156
157 let result = accept!(&request,
158 "text/plain" => 5,
159 "text/plain" => 12,
160 "text/plain" => 28,
161 );
162
163 assert_eq!(result, 5);
164 }
165
166 #[test]
167 fn no_header_first() {
168 let request = Request::fake_http("GET", "/", vec![], vec![]);
169
170 let result = accept!(&request,
171 "image/gif" => 5,
172 "text/plain" => 12,
173 "text/html" => 28,
174 );
175
176 assert_eq!(result, 5);
177 }
178
179 #[test]
180 fn no_header_wildcard() {
181 let request = Request::fake_http(
182 "GET",
183 "/",
184 vec![("Accept".to_owned(), "image/tiff".to_owned())],
185 vec![],
186 );
187
188 let result = accept!(&request,
189 "image/gif" => 5,
190 "text/plain" => 12,
191 "text/html" => 28,
192 "*/*" => 37
193 );
194
195 assert_eq!(result, 37);
196 }
197}