axum_core/
macros.rs

1/// Private API.
2#[cfg(feature = "tracing")]
3#[doc(hidden)]
4#[macro_export]
5macro_rules! __log_rejection {
6    (
7        rejection_type = $ty:ident,
8        body_text = $body_text:expr,
9        status = $status:expr,
10    ) => {
11        {
12            $crate::__private::tracing::event!(
13                target: "axum::rejection",
14                $crate::__private::tracing::Level::TRACE,
15                status = $status.as_u16(),
16                body = $body_text,
17                rejection_type = ::std::any::type_name::<$ty>(),
18                "rejecting request",
19            );
20        }
21    };
22}
23
24#[cfg(not(feature = "tracing"))]
25#[doc(hidden)]
26#[macro_export]
27macro_rules! __log_rejection {
28    (
29        rejection_type = $ty:ident,
30        body_text = $body_text:expr,
31        status = $status:expr,
32    ) => {};
33}
34
35/// Private API.
36#[doc(hidden)]
37#[macro_export]
38macro_rules! __define_rejection {
39    (
40        #[status = $status:ident]
41        #[body = $body:literal]
42        $(#[$m:meta])*
43        pub struct $name:ident;
44    ) => {
45        $(#[$m])*
46        #[derive(Debug)]
47        #[non_exhaustive]
48        pub struct $name;
49
50        impl $name {
51            /// Get the response body text used for this rejection.
52            pub fn body_text(&self) -> String {
53                self.to_string()
54            }
55
56            /// Get the status code used for this rejection.
57            pub fn status(&self) -> http::StatusCode {
58                http::StatusCode::$status
59            }
60        }
61
62        impl $crate::response::IntoResponse for $name {
63            fn into_response(self) -> $crate::response::Response {
64                let status = self.status();
65
66                $crate::__log_rejection!(
67                    rejection_type = $name,
68                    body_text = $body,
69                    status = status,
70                );
71                (status, $body).into_response()
72            }
73        }
74
75        impl std::fmt::Display for $name {
76            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77                write!(f, "{}", $body)
78            }
79        }
80
81        impl std::error::Error for $name {}
82
83        impl Default for $name {
84            fn default() -> Self {
85                Self
86            }
87        }
88    };
89
90    (
91        #[status = $status:ident]
92        #[body = $body:literal]
93        $(#[$m:meta])*
94        pub struct $name:ident (Error);
95    ) => {
96        $(#[$m])*
97        #[derive(Debug)]
98        pub struct $name(pub(crate) $crate::Error);
99
100        impl $name {
101            pub(crate) fn from_err<E>(err: E) -> Self
102            where
103                E: Into<$crate::BoxError>,
104            {
105                Self($crate::Error::new(err))
106            }
107
108            /// Get the response body text used for this rejection.
109            pub fn body_text(&self) -> String {
110                self.to_string()
111            }
112
113            /// Get the status code used for this rejection.
114            pub fn status(&self) -> http::StatusCode {
115                http::StatusCode::$status
116            }
117        }
118
119        impl $crate::response::IntoResponse for $name {
120            fn into_response(self) -> $crate::response::Response {
121                let status = self.status();
122                let body_text = self.body_text();
123
124                $crate::__log_rejection!(
125                    rejection_type = $name,
126                    body_text = body_text,
127                    status = status,
128                );
129                (status, body_text).into_response()
130            }
131        }
132
133        impl std::fmt::Display for $name {
134            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135                f.write_str($body)?;
136                f.write_str(": ")?;
137                self.0.fmt(f)
138            }
139        }
140
141        impl std::error::Error for $name {
142            fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
143                Some(&self.0)
144            }
145        }
146    };
147}
148
149/// Private API.
150#[doc(hidden)]
151#[macro_export]
152macro_rules! __composite_rejection {
153    (
154        $(#[$m:meta])*
155        pub enum $name:ident {
156            $($variant:ident),+
157            $(,)?
158        }
159    ) => {
160        $(#[$m])*
161        #[derive(Debug)]
162        #[non_exhaustive]
163        pub enum $name {
164            $(
165                #[allow(missing_docs)]
166                $variant($variant)
167            ),+
168        }
169
170        impl $crate::response::IntoResponse for $name {
171            fn into_response(self) -> $crate::response::Response {
172                match self {
173                    $(
174                        Self::$variant(inner) => inner.into_response(),
175                    )+
176                }
177            }
178        }
179
180        impl $name {
181            /// Get the response body text used for this rejection.
182            pub fn body_text(&self) -> String {
183                match self {
184                    $(
185                        Self::$variant(inner) => inner.body_text(),
186                    )+
187                }
188            }
189
190            /// Get the status code used for this rejection.
191            pub fn status(&self) -> http::StatusCode {
192                match self {
193                    $(
194                        Self::$variant(inner) => inner.status(),
195                    )+
196                }
197            }
198        }
199
200        $(
201            impl From<$variant> for $name {
202                fn from(inner: $variant) -> Self {
203                    Self::$variant(inner)
204                }
205            }
206        )+
207
208        impl std::fmt::Display for $name {
209            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210                match self {
211                    $(
212                        Self::$variant(inner) => write!(f, "{inner}"),
213                    )+
214                }
215            }
216        }
217
218        impl std::error::Error for $name {
219            fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
220                match self {
221                    $(
222                        Self::$variant(inner) => inner.source(),
223                    )+
224                }
225            }
226        }
227    };
228}
229
230#[rustfmt::skip]
231macro_rules! all_the_tuples {
232    ($name:ident) => {
233        $name!([], T1);
234        $name!([T1], T2);
235        $name!([T1, T2], T3);
236        $name!([T1, T2, T3], T4);
237        $name!([T1, T2, T3, T4], T5);
238        $name!([T1, T2, T3, T4, T5], T6);
239        $name!([T1, T2, T3, T4, T5, T6], T7);
240        $name!([T1, T2, T3, T4, T5, T6, T7], T8);
241        $name!([T1, T2, T3, T4, T5, T6, T7, T8], T9);
242        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9], T10);
243        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T11);
244        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], T12);
245        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], T13);
246        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], T14);
247        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], T15);
248        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], T16);
249    };
250}
251
252macro_rules! all_the_tuples_no_last_special_case {
253    ($name:ident) => {
254        $name!(T1);
255        $name!(T1, T2);
256        $name!(T1, T2, T3);
257        $name!(T1, T2, T3, T4);
258        $name!(T1, T2, T3, T4, T5);
259        $name!(T1, T2, T3, T4, T5, T6);
260        $name!(T1, T2, T3, T4, T5, T6, T7);
261        $name!(T1, T2, T3, T4, T5, T6, T7, T8);
262        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
263        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
264        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
265        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
266        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
267        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
268        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
269        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
270    };
271}
272
273/// Private API.
274#[doc(hidden)]
275#[macro_export]
276macro_rules! __impl_deref {
277    ($ident:ident) => {
278        impl<T> std::ops::Deref for $ident<T> {
279            type Target = T;
280
281            #[inline]
282            fn deref(&self) -> &Self::Target {
283                &self.0
284            }
285        }
286
287        impl<T> std::ops::DerefMut for $ident<T> {
288            #[inline]
289            fn deref_mut(&mut self) -> &mut Self::Target {
290                &mut self.0
291            }
292        }
293    };
294
295    ($ident:ident: $ty:ty) => {
296        impl std::ops::Deref for $ident {
297            type Target = $ty;
298
299            #[inline]
300            fn deref(&self) -> &Self::Target {
301                &self.0
302            }
303        }
304
305        impl std::ops::DerefMut for $ident {
306            #[inline]
307            fn deref_mut(&mut self) -> &mut Self::Target {
308                &mut self.0
309            }
310        }
311    };
312}
313
314#[cfg(test)]
315mod composite_rejection_tests {
316    use self::defs::*;
317    use crate::Error;
318    use std::error::Error as _;
319
320    #[allow(dead_code, unreachable_pub)]
321    mod defs {
322        __define_rejection! {
323            #[status = BAD_REQUEST]
324            #[body = "error message 1"]
325            pub struct Inner1;
326        }
327        __define_rejection! {
328            #[status = BAD_REQUEST]
329            #[body = "error message 2"]
330            pub struct Inner2(Error);
331        }
332        __composite_rejection! {
333            pub enum Outer { Inner1, Inner2 }
334        }
335    }
336
337    /// The implementation of `.source()` on `Outer` should defer straight to the implementation
338    /// on its inner type instead of returning the inner type itself, because the `Display`
339    /// implementation on `Outer` already forwards to the inner type and so it would result in two
340    /// errors in the chain `Display`ing the same thing.
341    #[test]
342    fn source_gives_inner_source() {
343        let rejection = Outer::Inner1(Inner1);
344        assert!(rejection.source().is_none());
345
346        let msg = "hello world";
347        let rejection = Outer::Inner2(Inner2(Error::new(msg)));
348        assert_eq!(rejection.source().unwrap().to_string(), msg);
349    }
350}