actix_web/http/header/
macros.rs

1macro_rules! common_header_test_module {
2    ($id:ident, $tm:ident{$($tf:item)*}) => {
3        #[cfg(test)]
4        mod $tm {
5            #![allow(unused_imports)]
6
7            use ::core::str;
8
9            use ::actix_http::{Method, test};
10            use ::mime::*;
11
12            use $crate::http::header::{self, *};
13            use super::{$id as HeaderField, *};
14
15            $($tf)*
16        }
17    }
18}
19
20#[cfg(test)]
21macro_rules! common_header_test {
22    ($id:ident, $raw:expr) => {
23        #[test]
24        fn $id() {
25            use ::actix_http::test;
26
27            let raw = $raw;
28            let headers = raw.iter().map(|x| x.to_vec()).collect::<Vec<_>>();
29
30            let mut req = test::TestRequest::default();
31
32            for item in headers {
33                req = req.append_header((HeaderField::name(), item)).take();
34            }
35
36            let req = req.finish();
37            let value = HeaderField::parse(&req);
38
39            let result = format!("{}", value.unwrap());
40            let expected = ::std::string::String::from_utf8(raw[0].to_vec()).unwrap();
41
42            let result_cmp: Vec<String> = result
43                .to_ascii_lowercase()
44                .split(' ')
45                .map(|x| x.to_owned())
46                .collect();
47            let expected_cmp: Vec<String> = expected
48                .to_ascii_lowercase()
49                .split(' ')
50                .map(|x| x.to_owned())
51                .collect();
52
53            assert_eq!(result_cmp.concat(), expected_cmp.concat());
54        }
55    };
56
57    ($id:ident, $raw:expr, $exp:expr) => {
58        #[test]
59        fn $id() {
60            use actix_http::test;
61
62            let headers = $raw.iter().map(|x| x.to_vec()).collect::<Vec<_>>();
63            let mut req = test::TestRequest::default();
64
65            for item in headers {
66                req.append_header((HeaderField::name(), item));
67            }
68
69            let req = req.finish();
70            let val = HeaderField::parse(&req);
71
72            let exp: ::core::option::Option<HeaderField> = $exp;
73
74            // test parsing
75            assert_eq!(val.ok(), exp);
76
77            // test formatting
78            if let Some(exp) = exp {
79                let raw = &($raw)[..];
80                let mut iter = raw.iter().map(|b| str::from_utf8(&b[..]).unwrap());
81                let mut joined = String::new();
82                if let Some(s) = iter.next() {
83                    joined.push_str(s);
84                    for s in iter {
85                        joined.push_str(", ");
86                        joined.push_str(s);
87                    }
88                }
89                assert_eq!(format!("{}", exp), joined);
90            }
91        }
92    };
93}
94
95macro_rules! common_header {
96    // TODO: these docs are wrong, there's no $n or $nn
97    // $attrs:meta: Attributes associated with the header item (usually docs)
98    // $id:ident: Identifier of the header
99    // $n:expr: Lowercase name of the header
100    // $nn:expr: Nice name of the header
101
102    // List header, zero or more items
103    ($(#[$attrs:meta])*($id:ident, $name:expr) => ($item:ty)*) => {
104        $(#[$attrs])*
105        #[derive(Debug, Clone, PartialEq, Eq, ::derive_more::Deref, ::derive_more::DerefMut)]
106        pub struct $id(pub Vec<$item>);
107
108        impl $crate::http::header::Header for $id {
109            #[inline]
110            fn name() -> $crate::http::header::HeaderName {
111                $name
112            }
113
114            #[inline]
115            fn parse<M: $crate::HttpMessage>(msg: &M) -> Result<Self, $crate::error::ParseError> {
116                let headers = msg.headers().get_all(Self::name());
117                $crate::http::header::from_comma_delimited(headers).map($id)
118            }
119        }
120
121        impl ::core::fmt::Display for $id {
122            #[inline]
123            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
124                $crate::http::header::fmt_comma_delimited(f, &self.0[..])
125            }
126        }
127
128        impl $crate::http::header::TryIntoHeaderValue for $id {
129            type Error = $crate::http::header::InvalidHeaderValue;
130
131            #[inline]
132            fn try_into_value(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {
133                use ::core::fmt::Write;
134                let mut writer = $crate::http::header::Writer::new();
135                let _ = write!(&mut writer, "{}", self);
136                $crate::http::header::HeaderValue::from_maybe_shared(writer.take())
137            }
138        }
139    };
140
141    // List header, one or more items
142    ($(#[$attrs:meta])*($id:ident, $name:expr) => ($item:ty)+) => {
143        $(#[$attrs])*
144        #[derive(Debug, Clone, PartialEq, Eq, ::derive_more::Deref, ::derive_more::DerefMut)]
145        pub struct $id(pub Vec<$item>);
146
147        impl $crate::http::header::Header for $id {
148            #[inline]
149            fn name() -> $crate::http::header::HeaderName {
150                $name
151            }
152
153            #[inline]
154            fn parse<M: $crate::HttpMessage>(msg: &M) -> Result<Self, $crate::error::ParseError>{
155                let headers = msg.headers().get_all(Self::name());
156
157                $crate::http::header::from_comma_delimited(headers)
158                    .and_then(|items| {
159                        if items.is_empty() {
160                            Err($crate::error::ParseError::Header)
161                        } else {
162                            Ok($id(items))
163                        }
164                    })
165            }
166        }
167
168        impl ::core::fmt::Display for $id {
169            #[inline]
170            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
171                $crate::http::header::fmt_comma_delimited(f, &self.0[..])
172            }
173        }
174
175        impl $crate::http::header::TryIntoHeaderValue for $id {
176            type Error = $crate::http::header::InvalidHeaderValue;
177
178            #[inline]
179            fn try_into_value(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {
180                use ::core::fmt::Write;
181                let mut writer = $crate::http::header::Writer::new();
182                let _ = write!(&mut writer, "{}", self);
183                $crate::http::header::HeaderValue::from_maybe_shared(writer.take())
184            }
185        }
186    };
187
188    // Single value header
189    ($(#[$attrs:meta])*($id:ident, $name:expr) => [$value:ty]) => {
190        $(#[$attrs])*
191        #[derive(Debug, Clone, PartialEq, Eq, ::derive_more::Deref, ::derive_more::DerefMut)]
192        pub struct $id(pub $value);
193
194        impl $crate::http::header::Header for $id {
195            #[inline]
196            fn name() -> $crate::http::header::HeaderName {
197                $name
198            }
199
200            #[inline]
201            fn parse<M: $crate::HttpMessage>(msg: &M) -> Result<Self, $crate::error::ParseError> {
202                let header = msg.headers().get(Self::name());
203                $crate::http::header::from_one_raw_str(header).map($id)
204            }
205        }
206
207        impl ::core::fmt::Display for $id {
208            #[inline]
209            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
210                ::core::fmt::Display::fmt(&self.0, f)
211            }
212        }
213
214        impl $crate::http::header::TryIntoHeaderValue for $id {
215            type Error = $crate::http::header::InvalidHeaderValue;
216
217            #[inline]
218            fn try_into_value(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {
219                self.0.try_into_value()
220            }
221        }
222    };
223
224    // List header, one or more items with "*" option
225    ($(#[$attrs:meta])*($id:ident, $name:expr) => {Any / ($item:ty)+}) => {
226        $(#[$attrs])*
227        #[derive(Clone, Debug, PartialEq, Eq)]
228        pub enum $id {
229            /// Any value is a match
230            Any,
231
232            /// Only the listed items are a match
233            Items(Vec<$item>),
234        }
235
236        impl $crate::http::header::Header for $id {
237            #[inline]
238            fn name() -> $crate::http::header::HeaderName {
239                $name
240            }
241
242            #[inline]
243            fn parse<M: $crate::HttpMessage>(msg: &M) -> Result<Self, $crate::error::ParseError> {
244                let is_any = msg
245                    .headers()
246                    .get(Self::name())
247                    .and_then(|hdr| hdr.to_str().ok())
248                    .map(|hdr| hdr.trim() == "*");
249
250                if let Some(true) = is_any {
251                    Ok($id::Any)
252                } else {
253                    let headers = msg.headers().get_all(Self::name());
254                    Ok($id::Items($crate::http::header::from_comma_delimited(headers)?))
255                }
256            }
257        }
258
259        impl ::core::fmt::Display for $id {
260            #[inline]
261            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
262                match *self {
263                    $id::Any => f.write_str("*"),
264                    $id::Items(ref fields) =>
265                        $crate::http::header::fmt_comma_delimited(f, &fields[..])
266                }
267            }
268        }
269
270        impl $crate::http::header::TryIntoHeaderValue for $id {
271            type Error = $crate::http::header::InvalidHeaderValue;
272
273            #[inline]
274            fn try_into_value(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {
275                use ::core::fmt::Write;
276                let mut writer = $crate::http::header::Writer::new();
277                let _ = write!(&mut writer, "{}", self);
278                $crate::http::header::HeaderValue::from_maybe_shared(writer.take())
279            }
280        }
281    };
282
283    // optional test module
284    ($(#[$attrs:meta])*($id:ident, $name:expr) => ($item:ty)* $tm:ident{$($tf:item)*}) => {
285        crate::http::header::common_header! {
286            $(#[$attrs])*
287            ($id, $name) => ($item)*
288        }
289
290        crate::http::header::common_header_test_module! { $id, $tm { $($tf)* }}
291    };
292    ($(#[$attrs:meta])*($id:ident, $n:expr) => ($item:ty)+ $tm:ident{$($tf:item)*}) => {
293        crate::http::header::common_header! {
294            $(#[$attrs])*
295            ($id, $n) => ($item)+
296        }
297
298        crate::http::header::common_header_test_module! { $id, $tm { $($tf)* }}
299    };
300    ($(#[$attrs:meta])*($id:ident, $name:expr) => [$item:ty] $tm:ident{$($tf:item)*}) => {
301        crate::http::header::common_header! {
302            $(#[$attrs])* ($id, $name) => [$item]
303        }
304
305        crate::http::header::common_header_test_module! { $id, $tm { $($tf)* }}
306    };
307    ($(#[$attrs:meta])*($id:ident, $name:expr) => {Any / ($item:ty)+} $tm:ident{$($tf:item)*}) => {
308        crate::http::header::common_header! {
309            $(#[$attrs])*
310            ($id, $name) => {Any / ($item)+}
311        }
312
313        crate::http::header::common_header_test_module! { $id, $tm { $($tf)* }}
314    };
315}
316
317pub(crate) use common_header;
318#[cfg(test)]
319pub(crate) use common_header_test;
320pub(crate) use common_header_test_module;